/*****************************************************************************
 *
 * STATUSMAP.C - Nagios Network Status Map CGI
 *
 * Copyright (c) 1999-2006 Ethan Galstad (nagios@nagios.org)
 * Last Modified: 03-21-2006
 *
 * Description:
 *
 * This CGI will create a map of all hosts that are being monitored on your
 * network.
 *
 * License:
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *****************************************************************************/
/*************************************************************************
 * Translation to Chinese by 田朝阳(zytian@gmail.com)
 * $Id: nagios-2.7-zh_CN.patch $
 * !注意系统环境变量须设置为utf-8才可以正确显示中文。
 * !系统提供了SVG格式的输出格式，因没有测试不推荐使用，编程者也要慎用！
 **************************************************************************/

#include "../include/config.h"
#include "../include/common.h"
#include "../include/objects.h"
#include "../include/statusdata.h"

#include "../include/cgiutils.h"
#include "../include/getcgi.h"
#include "../include/cgiauth.h"

#include <gd.h>			/* Boutell's GD library function */
#include <gdfonts.h>		/* GD library small font definition */

extern int             refresh_rate;

/*#define DEBUG*/

#define UNKNOWN_GD2_ICON      "unknown.gd2"
#define UNKNOWN_ICON_IMAGE    "unknown.gif"
#define NAGIOS_GD2_ICON       "nagios.gd2"

extern char main_config_file[MAX_FILENAME_LENGTH];
extern char physical_images_path[MAX_FILENAME_LENGTH];
extern char url_images_path[MAX_FILENAME_LENGTH];
extern char url_logo_images_path[MAX_FILENAME_LENGTH];
extern char url_stylesheets_path[MAX_FILENAME_LENGTH];
extern char ttf_file[MAX_FILENAME_LENGTH];
extern host *host_list;
extern hostgroup *hostgroup_list;
extern service *service_list;
extern hoststatus *hoststatus_list;
extern servicestatus *servicestatus_list;
extern int statusmap_mod;
extern char *statusmap_background_image;

extern int default_statusmap_layout_method;

#define DEFAULT_NODE_WIDTH		40
#define DEFAULT_NODE_HEIGHT		65

#define DEFAULT_NODE_VSPACING           15
#define DEFAULT_NODE_HSPACING           45

#define DEFAULT_PROXIMITY_WIDTH		1000
#define DEFAULT_PROXIMITY_HEIGHT	800

#define MINIMUM_PROXIMITY_WIDTH         250
#define MINIMUM_PROXIMITY_HEIGHT        200

#define COORDS_WARNING_WIDTH            650
#define COORDS_WARNING_HEIGHT           60

#define CIRCULAR_DRAWING_RADIUS         100

#define CREATE_HTML	0
#define CREATE_IMAGE	1

#define LAYOUT_USER_SUPPLIED            0
#define LAYOUT_SUBLAYERS                1
#define LAYOUT_COLLAPSED_TREE           2
#define LAYOUT_BALANCED_TREE            3
#define LAYOUT_CIRCULAR                 4
#define LAYOUT_CIRCULAR_MARKUP          5
#define LAYOUT_CIRCULAR_BALLOON         6


typedef struct layer_struct{
	char *layer_name;
	struct layer_struct *next;
        }layer;


void document_header(int);
void document_footer(void);
int process_cgivars(void);

void display_page_header(void);
void display_map(void);
void calculate_host_coords(void);
void calculate_total_image_bounds(void);
void calculate_canvas_bounds(void);
void calculate_canvas_bounds_from_host(char *);
void calculate_scaling_factor(void);
void find_eligible_hosts(void);
void load_background_image(void);
void draw_background_image(void);
void draw_background_extras(void);
void draw_host_links(void);
void draw_hosts(void);
void draw_host_text(char *,int,int);
void draw_text(char *,int,int,int);
void write_popup_code(void);
void write_host_popup_text(host *);

int initialize_graphics(void);
gdImagePtr load_image_from_file(char *);
void write_graphics(void);
void cleanup_graphics(void);
void draw_line(int,int,int,int,int);
void draw_dotted_line(int,int,int,int,int);
void draw_dashed_line(int,int,int,int,int);

int is_host_in_layer_list(host *);
int add_layer(char *);
void free_layer_list(void);
void print_layer_url(int);

int number_of_host_layer_members(host *,int);
int max_child_host_layer_members(host *);
int host_child_depth_separation(host *, host *);
int max_child_host_drawing_width(host *);
int number_of_host_services(host *);

void calculate_balanced_tree_coords(host *,int,int);
void calculate_circular_coords(void);
void calculate_circular_layer_coords(host *,double,double,int,int);

void draw_circular_markup(void);
void draw_circular_layer_markup(host *,double,double,int,int);


char physical_logo_images_path[MAX_FILENAME_LENGTH];

authdata current_authdata;

int create_type=CREATE_HTML;

gdImagePtr unknown_logo_image=NULL;
gdImagePtr logo_image=NULL;
gdImagePtr map_image=NULL;
gdImagePtr background_image=NULL;
int color_white=0xFFFFFF;
int color_black=0x000000;
int color_red=0xFF0000;
int color_lightred=0xF0000;
int color_green=0x00FF00;
int color_lightgreen=0x0F0F00;
int color_blue=0x000FF00;
int color_yellow=0xFFFF00;
int color_orange=0x0F0F00;
int color_grey=0x0F0F0F;
int color_lightgrey=0x080808;

int show_all_hosts=TRUE;
char *host_name="all";

int embedded=FALSE;
int display_header=TRUE;
int display_popups=TRUE;
int use_links=TRUE;
int use_text=TRUE;
int use_highlights=TRUE;
int user_supplied_canvas=FALSE;
int user_supplied_scaling=FALSE;

int layout_method=LAYOUT_USER_SUPPLIED;

int proximity_width=DEFAULT_PROXIMITY_WIDTH;
int proximity_height=DEFAULT_PROXIMITY_HEIGHT;

int coordinates_were_specified=FALSE;   /* were any coordinates specified in extended host information entries? */

int scaled_image_width=0;        /* size of the image actually displayed on the screen (after scaling) */
int scaled_image_height=0;
int canvas_width=0;              /* actual size of the image (or portion thereof) that we are drawing */
int canvas_height=0;
int total_image_width=0;         /* actual size of the image that would be created if we drew all hosts */
int total_image_height=0;
int max_image_width=0;           /* max image size the user wants (scaled) */
int max_image_height=0;
double scaling_factor=1.0;       /* scaling factor to use */
double user_scaling_factor=1.0;  /* user-supplied scaling factor */
int background_image_width=0;
int background_image_height=0;

int canvas_x=0;                     /* upper left coords of drawing canvas */
int canvas_y=0;

int bottom_margin=0;

int draw_child_links=FALSE;
int draw_parent_links=FALSE;

int draw_nagios_icon=FALSE;    /* should we drawn the Nagios process icon? */
int nagios_icon_x=0;           /* coords of Nagios icon */
int nagios_icon_y=0;

extern hostextinfo *hostextinfo_list;
extern hoststatus *hoststatus_list;

extern time_t program_start;

layer *layer_list=NULL;
int exclude_layers=TRUE;
int all_layers=FALSE;

FILE *image_output_file=NULL;





int main(int argc, char **argv){
	int result;

	/* reset internal variables */
	reset_cgi_vars();

	/* read the CGI configuration file */
	result=read_cgi_config_file(get_cgi_config_location());
	if(result==ERROR){
		document_header(FALSE);
		if(create_type==CREATE_HTML)
			cgi_config_file_error(get_cgi_config_location());
		document_footer();
		return ERROR;
	        }

	/* defaults from CGI config file */
	layout_method=default_statusmap_layout_method;

	/* get the arguments passed in the URL */
	process_cgivars();

	/* read the main configuration file */
	result=read_main_config_file(main_config_file);
	if(result==ERROR){
		document_header(FALSE);
		if(create_type==CREATE_HTML)
			main_config_file_error(main_config_file);
		document_footer();
		return ERROR;
	        }

	/* read all object configuration data */
	result=read_all_object_configuration_data(main_config_file,READ_ALL_OBJECT_DATA);
	if(result==ERROR){
		document_header(FALSE);
		if(create_type==CREATE_HTML)
			object_data_error();
		document_footer();
		return ERROR;
                }

	/* read all status data */
	result=read_all_status_data(get_cgi_config_location(),READ_ALL_STATUS_DATA);
	if(result==ERROR){
		document_header(FALSE);
		if(create_type==CREATE_HTML)
			status_data_error();
		document_footer();
		free_memory();
		return ERROR;
                }


	document_header(TRUE);

	/* get authentication information */
	get_authentication_information(&current_authdata);

	/* display the network map... */
	display_map();

	document_footer();

	/* free all allocated memory */
	free_memory();
	free_layer_list();

	return OK;
        }



void document_header(int use_stylesheet){
	char date_time[MAX_DATETIME_LENGTH];
	time_t current_time;
	time_t expire_time;

	if(create_type==CREATE_HTML){
		printf("Cache-Control: no-store\r\n");
		printf("Pragma: no-cache\r\n");
		printf("Refresh: %d\r\n",refresh_rate);

		time(&current_time);
		get_time_string(&current_time,date_time,sizeof(date_time),HTTP_DATE_TIME);
		printf("Last-Modified: %s\r\n",date_time);

		expire_time=0L;
		get_time_string(&expire_time,date_time,sizeof(date_time),HTTP_DATE_TIME);
		printf("Expires: %s\r\n",date_time);
		//if( 2 != statusmap_mod){
			printf("Content-Type: text/html\r\n\r\n");
		//}else{
		//	printf("Content-Type: image/svg+xml\r\n\r\n");
		//}
		if(embedded==TRUE)
			return;

		printf("<html>\n");
		printf("<head>\n");
		printf("<meta http-equiv='content-type' content='text/html;charset=UTF-8'>\n");
		printf("<title>\n");
		printf("网络图\n");
		printf("</title>\n");

		if(use_stylesheet==TRUE){
			printf("<LINK REL='stylesheet' TYPE='text/css' HREF='%s%s'>\n",url_stylesheets_path,COMMON_CSS);
			printf("<LINK REL='stylesheet' TYPE='text/css' HREF='%s%s'>\n",url_stylesheets_path,STATUSMAP_CSS);
		        }

		/* write JavaScript code for popup window */
		write_popup_code();

		printf("</head>\n");

		printf("<body CLASS='statusmap' name='mappage' id='mappage'>\n");

		/* include user SSI header */
		include_ssi_files(STATUSMAP_CGI,SSI_HEADER);

		printf("<div id=\"popup\" style=\"position:absolute; z-index:1; visibility: hidden\"></div>\n");
	        }

	else{
		printf("Cache-Control: no-store\r\n");
		printf("Pragma: no-cache\r\n");

		time(&current_time);
		get_time_string(&current_time,date_time,sizeof(date_time),HTTP_DATE_TIME);
		printf("Last-Modified: %s\r\n",date_time);

		expire_time=(time_t)0L;
		get_time_string(&expire_time,date_time,sizeof(date_time),HTTP_DATE_TIME);
		printf("Expires: %s\r\n",date_time);
		if( 2 != statusmap_mod){
			printf("Content-Type: image/png\r\n\r\n");
		}else{
			printf("Content-Type: image/svg+xml\r\n\r\n");
		}
	        }

	return;
        }


void document_footer(void){

	if(embedded==TRUE)
		return;

	if(create_type==CREATE_HTML){

		/* include user SSI footer */
		include_ssi_files(STATUSMAP_CGI,SSI_FOOTER);

		printf("</body>\n");
		printf("</html>\n");
	        }

	return;
        }



int process_cgivars(void){
	char **variables;
	int error=FALSE;
	int x;

	variables=getcgivars();

	for(x=0;variables[x]!=NULL;x++){

		/* do some basic length checking on the variable identifier to prevent buffer overflows */
		if(strlen(variables[x])>=MAX_INPUT_BUFFER-1){
			x++;
			continue;
		        }

		/* we found the host argument */
		else if(!strcmp(variables[x],"host")){
			x++;
			if(variables[x]==NULL){
				error=TRUE;
				break;
			        }

			host_name=(char *)malloc(strlen(variables[x])+1);
			if(host_name==NULL)
				host_name="all";
			else
				strcpy(host_name,variables[x]);

			if(!strcmp(host_name,"all"))
				show_all_hosts=TRUE;
			else
				show_all_hosts=FALSE;
		        }

		/* we found the image creation option */
		else if(!strcmp(variables[x],"createimage")){
			create_type=CREATE_IMAGE;
		        }

		/* we found the embed option */
		else if(!strcmp(variables[x],"embedded"))
			embedded=TRUE;

		/* we found the noheader option */
		else if(!strcmp(variables[x],"noheader"))
			display_header=FALSE;

		/* we found the canvas origin */
		else if(!strcmp(variables[x],"canvas_x")){
			x++;
			if(variables[x]==NULL){
				error=TRUE;
				break;
			        }
			canvas_x=atoi(variables[x]);
			user_supplied_canvas=TRUE;
		        }
		else if(!strcmp(variables[x],"canvas_y")){
			x++;
			if(variables[x]==NULL){
				error=TRUE;
				break;
			        }
			canvas_y=atoi(variables[x]);
			user_supplied_canvas=TRUE;
		        }

		/* we found the canvas size */
		else if(!strcmp(variables[x],"canvas_width")){
			x++;
			if(variables[x]==NULL){
				error=TRUE;
				break;
			        }
			canvas_width=atoi(variables[x]);
			user_supplied_canvas=TRUE;
		        }
		else if(!strcmp(variables[x],"canvas_height")){
			x++;
			if(variables[x]==NULL){
				error=TRUE;
				break;
			        }
			canvas_height=atoi(variables[x]);
			user_supplied_canvas=TRUE;
		        }
		else if(!strcmp(variables[x],"proximity_width")){
			x++;
			if(variables[x]==NULL){
				error=TRUE;
				break;
			        }
			proximity_width=atoi(variables[x]);
			if(proximity_width<0)
				proximity_width=DEFAULT_PROXIMITY_WIDTH;
		        }
		else if(!strcmp(variables[x],"proximity_height")){
			x++;
			if(variables[x]==NULL){
				error=TRUE;
				break;
			        }
			proximity_height=atoi(variables[x]);
			if(proximity_height<0)
				proximity_height=DEFAULT_PROXIMITY_HEIGHT;
		        }

		/* we found the scaling factor */
		else if(!strcmp(variables[x],"scaling_factor")){
			x++;
			if(variables[x]==NULL){
				error=TRUE;
				break;
			        }
			user_scaling_factor=strtod(variables[x],NULL);
			if(user_scaling_factor>0.0)
				user_supplied_scaling=TRUE;
		        }

		/* we found the max image size */
		else if(!strcmp(variables[x],"max_width")){
			x++;
			if(variables[x]==NULL){
				error=TRUE;
				break;
			        }
			max_image_width=atoi(variables[x]);
		        }
		else if(!strcmp(variables[x],"max_height")){
			x++;
			if(variables[x]==NULL){
				error=TRUE;
				break;
			        }
			max_image_height=atoi(variables[x]);
		        }

		/* we found the layout method option */
		else if(!strcmp(variables[x],"layout")){
			x++;
			if(variables[x]==NULL){
				error=TRUE;
				break;
			        }
			layout_method=atoi(variables[x]);
		        }

		/* we found the no links argument*/
		else if(!strcmp(variables[x],"nolinks"))
			use_links=FALSE;

		/* we found the no text argument*/
		else if(!strcmp(variables[x],"notext"))
			use_text=FALSE;

		/* we found the no highlights argument*/
		else if(!strcmp(variables[x],"nohighlights"))
			use_highlights=FALSE;

		/* we found the no popups argument*/
		else if(!strcmp(variables[x],"nopopups"))
			display_popups=FALSE;

		/* we found the layer inclusion/exclusion argument */
		else if(!strcmp(variables[x],"layermode")){
			x++;
			if(variables[x]==NULL){
				error=TRUE;
				break;
			        }

			if(!strcmp(variables[x],"include"))
				exclude_layers=FALSE;
			else
				exclude_layers=TRUE;
		        }

		/* we found the layer argument */
		else if(!strcmp(variables[x],"layer")){
			x++;
			if(variables[x]==NULL){
				error=TRUE;
				break;
			        }

			add_layer(variables[x]);
		        }
	        }

	/* free memory allocated to the CGI variables */
	free_cgivars(variables);

	return error;
        }



/* top of page */
void display_page_header(void){
	char temp_buffer[MAX_INPUT_BUFFER];
	int zoom;
	int zoom_width, zoom_height;
	int zoom_width_granularity=0;
	int zoom_height_granularity=0;
	int current_zoom_granularity=0;
	hostgroup *temp_hostgroup;
	layer *temp_layer;
	int found=0;


	if(create_type!=CREATE_HTML)
		return;

	if(display_header==TRUE){

		/* begin top table */
		printf("<table border=0 width=100%% cellspacing=0 cellpadding=0>\n");
		printf("<tr>\n");

		/* left column of the first row */
		printf("<td align=left valign=top>\n");

		if(show_all_hosts==TRUE)
			snprintf(temp_buffer,sizeof(temp_buffer)-1,"所有主机的网络图");
		else
			snprintf(temp_buffer,sizeof(temp_buffer)-1,"<I>%s</I>的网络图",host_name);
		temp_buffer[sizeof(temp_buffer)-1]='\x0';
		display_info_table(temp_buffer,TRUE,&current_authdata);

		printf("<TABLE BORDER=1 CELLPADDING=0 CELLSPACING=0 CLASS='linkBox'>\n");
		printf("<TR><TD CLASS='linkBox'>\n");

		if(show_all_hosts==FALSE){
			printf("<a href='%s?host=all&max_width=%d&max_height=%d'>所有主机的状态图</a><BR>",STATUSMAP_CGI,max_image_width,max_image_height);
			printf("<a href='%s?host=%s'>该主机的详细状态</a><BR>\n",STATUS_CGI,url_encode(host_name));
		        }
		printf("<a href='%s?host=all'>所有主机的详细状态</a><BR>\n",STATUS_CGI);
		printf("<a href='%s?hostgroup=all'>所有主机的概要状态</a>\n",STATUS_CGI);

		printf("</TD></TR>\n");
		printf("</TABLE>\n");

		printf("</td>\n");



		/* center column of top row */
		printf("<td align=center valign=center>\n");

		/* print image size and scaling info */
#ifdef DEBUG
		printf("<p><div align=center><font size=-1>\n");
		printf("[ Raw Image Size: %d x %d pixels | Scaling Factor: %1.2lf | Scaled Image Size: %d x %d pixels ]",canvas_width,canvas_height,scaling_factor,(int)(canvas_width*scaling_factor),(int)(canvas_height*scaling_factor));
		printf("</font></div></p>\n");

		printf("<p><div align=center><font size=-1>\n");
		printf("[ Canvas_x: %d | Canvas_y: %d | Canvas_width: %d | Canvas_height: %d ]",canvas_x,canvas_y,canvas_width,canvas_height);
		printf("</font></div></p>\n");
#endif

		/* zoom links */
		if(user_supplied_canvas==FALSE && strcmp(host_name,"all") && display_header==TRUE){

			printf("<p><div align=center>\n");

			zoom_width_granularity=((total_image_width-MINIMUM_PROXIMITY_WIDTH)/11);
			if(zoom_width_granularity==0)
				zoom_width_granularity=1;
			zoom_height_granularity=((total_image_height-MINIMUM_PROXIMITY_HEIGHT)/11);

			if(proximity_width<=0)
				current_zoom_granularity=0;
			else
				current_zoom_granularity=(total_image_width-proximity_width)/zoom_width_granularity;
			if(current_zoom_granularity>10)
				current_zoom_granularity=10;

			printf("<table border=0 cellpadding=0 cellspacing=2>\n");
			printf("<tr>\n");
			printf("<td valign=center class='zoomTitle'>缩小(Zoom Out)&nbsp;&nbsp;</td>\n");

			for(zoom=0;zoom<=10;zoom++){

				zoom_width=total_image_width-(zoom*zoom_width_granularity);
				zoom_height=total_image_height-(zoom*zoom_height_granularity);

				printf("<td valign=center><a href='%s?host=%s&layout=%d&max_width=%d&max_height=%d&proximity_width=%d&proximity_height=%d%s%s",STATUSMAP_CGI,url_encode(host_name),layout_method,max_image_width,max_image_height,zoom_width,zoom_height,(display_header==TRUE)?"":"&noheader",(display_popups==FALSE)?"&nopopups":"");
				if(user_supplied_scaling==TRUE)
					printf("&scaling_factor=%2.1f",user_scaling_factor);
				print_layer_url(TRUE);
				printf("'>");
				printf("<img src='%s%s' border=0 alt='%d' title='%d'></a></td>\n",url_images_path,(current_zoom_granularity==zoom)?ZOOM2_ICON:ZOOM1_ICON,zoom,zoom);
		                }

			printf("<td valign=center class='zoomTitle'>&nbsp;&nbsp;放大(Zoom In)</td>\n");
			printf("</tr>\n");
			printf("</table>\n");

			printf("</div></p>\n");
	                }

		printf("</td>\n");



		/* right hand column of top row */
		printf("<td align=right valign=top>\n");

		printf("<table border=0 CLASS='optBox'>\n");
		printf("<tr><td valign=top>\n");
		printf("<form method=\"POST\" action=\"%s\">\n",STATUSMAP_CGI);
		printf("<input type='hidden' name='host' value='%s'>\n",host_name);
		printf("<input type='hidden' name='layout' value='%d'>\n",layout_method);

		printf("</td><td valign=top>\n");

		printf("<table border=0>\n");

		printf("<tr><td CLASS='optBoxItem'>\n");
		printf("展示方式:<br>\n");
		printf("<select name='layout'>\n");
#ifndef DUMMY_INSTALL
		printf("<option value=%d %s>用户定义坐标\n",LAYOUT_USER_SUPPLIED,(layout_method==LAYOUT_USER_SUPPLIED)?"selected":"");
#endif
		printf("<option value=%d %s>深度分层\n",LAYOUT_SUBLAYERS,(layout_method==LAYOUT_SUBLAYERS)?"selected":"");
		printf("<option value=%d %s>折叠图\n",LAYOUT_COLLAPSED_TREE,(layout_method==LAYOUT_COLLAPSED_TREE)?"selected":"");
		printf("<option value=%d %s>平衡折叠图\n",LAYOUT_BALANCED_TREE,(layout_method==LAYOUT_BALANCED_TREE)?"selected":"");
		printf("<option value=%d %s>圆形图\n",LAYOUT_CIRCULAR,(layout_method==LAYOUT_CIRCULAR)?"selected":"");
		printf("<option value=%d %s>圆形图(标记)\n",LAYOUT_CIRCULAR_MARKUP,(layout_method==LAYOUT_CIRCULAR_MARKUP)?"selected":"");
		printf("<option value=%d %s>圆形图(气球)\n",LAYOUT_CIRCULAR_BALLOON,(layout_method==LAYOUT_CIRCULAR_BALLOON)?"selected":"");
		printf("</select>\n");
		printf("</td>\n");
		printf("<td CLASS='optBoxItem'>\n");
		printf("缩放比例:<br>\n");
		printf("<input type='text' name='scaling_factor' maxlength='5' size='4' value='%2.1f'>\n",(user_supplied_scaling==TRUE)?user_scaling_factor:0.0);
		printf("</td></tr>\n");

		/*
		printf("<tr><td CLASS='optBoxItem'>\n");
		printf("最大图像宽度:<br>\n");
		printf("<input type='text' name='max_width' maxlength='5' size='4' value='%d'>\n",max_image_width);
		printf("</td>\n");
		printf("<td CLASS='optBoxItem'>\n");
		printf("最大图像高度:<br>\n");
		printf("<input type='text' name='max_height' maxlength='5' size='4' value='%d'>\n",max_image_height);
		printf("</td></tr>\n");

		printf("<tr><td CLASS='optBoxItem'>\n");
		printf("近似宽度:<br>\n");
		printf("<input type='text' name='proximity_width' maxlength='5' size='4' value='%d'>\n",proximity_width);
		printf("</td>\n");
		printf("<td CLASS='optBoxItem'>\n");
		printf("近似高度:<br>\n");
		printf("<input type='text' name='proximity_height' maxlength='5' size='4' value='%d'>\n",proximity_height);
		printf("</td></tr>\n");
		*/

		printf("<input type='hidden' name='max_width' value='%d'>\n",max_image_width);
		printf("<input type='hidden' name='max_height' value='%d'>\n",max_image_height);
		printf("<input type='hidden' name='proximity_width' value='%d'>\n",proximity_width);
		printf("<input type='hidden' name='proximity_height' value='%d'>\n",proximity_height);

		printf("<tr><td CLASS='optBoxItem'>图层:<br>\n");
		printf("<select multiple name='layer' size='4'>\n");
		for(temp_hostgroup=hostgroup_list;temp_hostgroup!=NULL;temp_hostgroup=temp_hostgroup->next){
			if(is_authorized_for_hostgroup(temp_hostgroup,&current_authdata)==FALSE)
				continue;
			found=0;
			for(temp_layer=layer_list;temp_layer!=NULL;temp_layer=temp_layer->next){
				if(!strcmp(temp_layer->layer_name,temp_hostgroup->group_name)){
					found=1;
					break;
				        }
			        }
			printf("<option value='%s' %s>%s\n",temp_hostgroup->group_name,(found==1)?"SELECTED":"",temp_hostgroup->alias);
		        }
		printf("</select>\n");
		printf("</td><td CLASS='optBoxItem' valign=top>图层模式:<br>");
		printf("<input type='radio' name='layermode' value='include' %s>包括<br>\n",(exclude_layers==FALSE)?"CHECKED":"");
		printf("<input type='radio' name='layermode' value='exclude' %s>排除\n",(exclude_layers==TRUE)?"CHECKED":"");
		printf("</td></tr>\n");

		printf("<tr><td CLASS='optBoxItem'>\n");
		printf("无弹出菜单:\n");
		printf("<input type='checkbox' name='nopopups' %s>\n",(display_popups==FALSE)?"CHECKED":"");
		printf("</td><td CLASS='optBoxItem'>\n");
		printf("<input type='submit' value='更新'>\n");
		printf("</td></tr>\n");

		/* display context-sensitive help */
		printf("<tr><td></td><td align=right valign=bottom>\n");
		display_context_help(CONTEXTHELP_MAP);
		printf("</td></tr>\n");

		printf("</table>\n");

		printf("</form>\n");
		printf("</td></tr>\n");
		printf("</table>\n");

		printf("</td>\n");

		/* end of top table */
		printf("</tr>\n");
		printf("</table>\n");
	        }


	return;
        }



/* top-level map generation... */
void display_map(void){

	load_background_image();
	calculate_host_coords();
	calculate_total_image_bounds();
	calculate_canvas_bounds();
	calculate_scaling_factor();
	find_eligible_hosts();

	/* display page header */
	display_page_header();

	initialize_graphics();
	draw_background_image();
	draw_background_extras();
	draw_host_links();

	if(create_type==CREATE_HTML)
		printf("<map name='statusmap'>\n");

	draw_hosts();

	if(create_type==CREATE_HTML)
		printf("</map>\n");

	write_graphics();
	cleanup_graphics();


	/* write the URL location for the image we just generated - the web browser will come and get it... */
	if(create_type==CREATE_HTML){
		if( 2 != statusmap_mod){
			printf("<P><DIV ALIGN=center>\n");
			printf("<img src='%s?host=%s&createimage",STATUSMAP_CGI,url_encode(host_name));
			printf("&canvas_x=%d&canvas_y=%d&canvas_width=%d&canvas_height=%d&max_width=%d&max_height=%d&layout=%d%s%s%s",canvas_x,canvas_y,canvas_width,canvas_height,max_image_width,max_image_height,layout_method,(use_links==FALSE)?"&nolinks":"",(use_text==FALSE)?"&notext":"",(use_highlights==FALSE)?"&nohighlights":"");
			print_layer_url(TRUE);
			printf("' width=%d height=%d border=0 name='statusimage' useMap='#statusmap'>\n",(int)(canvas_width*scaling_factor),(int)(canvas_height*scaling_factor));
			printf("</DIV></P>\n");
	    }else{
			printf("<P><DIV ALIGN=center>\n");
			printf("<object data='%s?host=%s&createimage",STATUSMAP_CGI,url_encode(host_name));
			printf("&canvas_x=%d&canvas_y=%d&canvas_width=%d&canvas_height=%d&max_width=%d&max_height=%d&layout=%d%s%s%s",canvas_x,canvas_y,canvas_width,canvas_height,max_image_width,max_image_height,layout_method,(use_links==FALSE)?"&nolinks":"",(use_text==FALSE)?"&notext":"",(use_highlights==FALSE)?"&nohighlights":"");
			print_layer_url(TRUE);
			printf("' width=%d height=%d border=0 name='statusimage' useMap='#statusmap' type='image/svg+xml'>\n",(int)(canvas_width*scaling_factor),(int)(canvas_height*scaling_factor));
			printf("</DIV></P>\n");
		}
	}
	return;
        }



/******************************************************************/
/********************* CALCULATION FUNCTIONS **********************/
/******************************************************************/

/* calculates host drawing coordinates */
void calculate_host_coords(void){
	hostextinfo *temp_hostextinfo;
	host *this_host;
	host *temp_host;
	int child_hosts=0;
	int parent_hosts=0;
	int max_layer_width=1;
	int current_child_host=0;
	int current_parent_host=0;
	int center_x=0;
	int offset_x=DEFAULT_NODE_WIDTH/2;
	int offset_y=DEFAULT_NODE_WIDTH/2;
	int current_layer=0;
	int layer_members=0;
	int current_layer_member=0;
	int max_drawing_width=0;


	/******************************/
	/***** MANUAL LAYOUT MODE *****/
	/******************************/

	/* user-supplied coords */
	if(layout_method==LAYOUT_USER_SUPPLIED){

		/* see which hosts we should draw and calculate drawing coords */
		for(temp_hostextinfo=hostextinfo_list;temp_hostextinfo!=NULL;temp_hostextinfo=temp_hostextinfo->next){

			if(temp_hostextinfo->have_2d_coords==TRUE)
				temp_hostextinfo->should_be_drawn=TRUE;
			else
				temp_hostextinfo->should_be_drawn=FALSE;
		        }

		return;
	        }


	/*****************************/
	/***** AUTO-LAYOUT MODES *****/
	/*****************************/

	/* add empty extended host info entries for all hosts that don't have any */
	for(temp_host=host_list;temp_host!=NULL;temp_host=temp_host->next){

		/* find the corresponding hostextinfo definition */
		temp_hostextinfo=find_hostextinfo(temp_host->name);

		/* none was found, so add a blank one */
		if(temp_hostextinfo==NULL)
			add_hostextinfo(temp_host->name,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0,0,0.0,0.0,0.0,0,0);
	        }


	/***** DEPTH LAYER MODE *****/
	if(layout_method==LAYOUT_SUBLAYERS){

		/* find the "main" host we're displaying */
		if(show_all_hosts==TRUE)
			this_host=NULL;
		else
			this_host=find_host(host_name);

		/* find total number of immediate parents/children for this host */
		child_hosts=number_of_immediate_child_hosts(this_host);
		parent_hosts=number_of_immediate_parent_hosts(this_host);

		if(child_hosts==0 && parent_hosts==0)
			max_layer_width=1;
		else
			max_layer_width=(child_hosts>parent_hosts)?child_hosts:parent_hosts;

		/* calculate center x coord */
		center_x=(((DEFAULT_NODE_WIDTH*max_layer_width)+(DEFAULT_NODE_HSPACING*(max_layer_width-1)))/2)+offset_x;

		/* coords for Nagios icon if necessary */
		if(this_host==NULL || this_host->parent_hosts==NULL){
			nagios_icon_x=center_x;
			nagios_icon_y=offset_y;
			draw_nagios_icon=TRUE;
		        }

		/* do we need to draw a link to parent(s)? */
		if(this_host!=NULL && is_host_immediate_child_of_host(NULL,this_host)==FALSE){
			draw_parent_links=TRUE;
			offset_y+=DEFAULT_NODE_HEIGHT+DEFAULT_NODE_VSPACING;
		        }

		/* see which hosts we should draw and calculate drawing coords */
		for(temp_hostextinfo=hostextinfo_list;temp_hostextinfo!=NULL;temp_hostextinfo=temp_hostextinfo->next){

			/* find the host that matches this entry */
			temp_host=find_host(temp_hostextinfo->host_name);

			if(temp_host==NULL)
				continue;

			/* this is an immediate parent of the "main" host we're drawing */
			else if(is_host_immediate_parent_of_host(this_host,temp_host)==TRUE){
				temp_hostextinfo->should_be_drawn=TRUE;
				temp_hostextinfo->have_2d_coords=TRUE;
				temp_hostextinfo->x_2d=center_x-(((parent_hosts*DEFAULT_NODE_WIDTH)+((parent_hosts-1)*DEFAULT_NODE_HSPACING))/2)+(current_parent_host*(DEFAULT_NODE_WIDTH+DEFAULT_NODE_HSPACING))+(DEFAULT_NODE_WIDTH/2);
				temp_hostextinfo->y_2d=offset_y;
				current_parent_host++;
			        }

			/* this is the "main" host we're drawing */
			else if(this_host==temp_host){
				temp_hostextinfo->should_be_drawn=TRUE;
				temp_hostextinfo->have_2d_coords=TRUE;
				temp_hostextinfo->x_2d=center_x;
				temp_hostextinfo->y_2d=DEFAULT_NODE_HEIGHT+DEFAULT_NODE_VSPACING+offset_y;
			        }

			/* this is an immediate child of the "main" host we're drawing */
			else if(is_host_immediate_child_of_host(this_host,temp_host)==TRUE){
				temp_hostextinfo->should_be_drawn=TRUE;
				temp_hostextinfo->have_2d_coords=TRUE;
				temp_hostextinfo->x_2d=center_x-(((child_hosts*DEFAULT_NODE_WIDTH)+((child_hosts-1)*DEFAULT_NODE_HSPACING))/2)+(current_child_host*(DEFAULT_NODE_WIDTH+DEFAULT_NODE_HSPACING))+(DEFAULT_NODE_WIDTH/2);
				if(this_host==NULL)
					temp_hostextinfo->y_2d=(DEFAULT_NODE_HEIGHT+DEFAULT_NODE_VSPACING)+offset_y;
				else
					temp_hostextinfo->y_2d=((DEFAULT_NODE_HEIGHT+DEFAULT_NODE_VSPACING)*2)+offset_y;
				current_child_host++;
				if(number_of_immediate_child_hosts(temp_host)>0){
					bottom_margin=DEFAULT_NODE_HEIGHT+DEFAULT_NODE_VSPACING;
					draw_child_links=TRUE;
				        }
			        }

			/* else do not draw this host */
			else{
				temp_hostextinfo->should_be_drawn=FALSE;
				temp_hostextinfo->have_2d_coords=FALSE;
			        }
		        }
	        }



	/***** COLLAPSED TREE MODE *****/
	else if(layout_method==LAYOUT_COLLAPSED_TREE){

		/* find the "main" host we're displaying  - DO NOT USE THIS (THIS IS THE OLD METHOD) */
		/*
		if(show_all_hosts==TRUE)
			this_host=NULL;
		else
			this_host=find_host(host_name);
		*/

		/* always use NULL as the "main" host, screen coords/dimensions are adjusted automatically */
		this_host=NULL;

		/* find total number of immediate parents for this host */
		parent_hosts=number_of_immediate_parent_hosts(this_host);

		/* find the max layer width we have... */
		max_layer_width=max_child_host_layer_members(this_host);
		if(parent_hosts>max_layer_width)
			max_layer_width=parent_hosts;

		/* calculate center x coord */
		center_x=(((DEFAULT_NODE_WIDTH*max_layer_width)+(DEFAULT_NODE_HSPACING*(max_layer_width-1)))/2)+offset_x;

		/* coords for Nagios icon if necessary */
		if(this_host==NULL || this_host->parent_hosts==NULL){
			nagios_icon_x=center_x;
			nagios_icon_y=offset_y;
			draw_nagios_icon=TRUE;
		        }

		/* do we need to draw a link to parent(s)? */
		if(this_host!=NULL && is_host_immediate_child_of_host(NULL,this_host)==FALSE){
			draw_parent_links=TRUE;
			offset_y+=DEFAULT_NODE_HEIGHT+DEFAULT_NODE_VSPACING;
		        }

		/* see which hosts we should draw and calculate drawing coords */
		for(temp_hostextinfo=hostextinfo_list;temp_hostextinfo!=NULL;temp_hostextinfo=temp_hostextinfo->next){

			/* find the host that matches this entry */
			temp_host=find_host(temp_hostextinfo->host_name);

			if(temp_host==NULL)
				continue;

			/* this is an immediate parent of the "main" host we're drawing */
			else if(is_host_immediate_parent_of_host(this_host,temp_host)==TRUE){
				temp_hostextinfo->should_be_drawn=TRUE;
				temp_hostextinfo->have_2d_coords=TRUE;
				temp_hostextinfo->x_2d=center_x-(((parent_hosts*DEFAULT_NODE_WIDTH)+((parent_hosts-1)*DEFAULT_NODE_HSPACING))/2)+(current_parent_host*(DEFAULT_NODE_WIDTH+DEFAULT_NODE_HSPACING))+(DEFAULT_NODE_WIDTH/2);
				temp_hostextinfo->y_2d=offset_y;
				current_parent_host++;
			        }

			/* this is the "main" host we're drawing */
			else if(this_host==temp_host){
				temp_hostextinfo->should_be_drawn=TRUE;
				temp_hostextinfo->have_2d_coords=TRUE;
				temp_hostextinfo->x_2d=center_x;
				temp_hostextinfo->y_2d=DEFAULT_NODE_HEIGHT+DEFAULT_NODE_VSPACING+offset_y;
			        }

			/* else do not draw this host (we might if its a child - see below, but assume no for now) */
			else{
				temp_hostextinfo->should_be_drawn=FALSE;
				temp_hostextinfo->have_2d_coords=FALSE;
			        }
		        }


		/* TODO: REORDER CHILD LAYER MEMBERS SO THAT WE MINIMIZE LINK CROSSOVERS FROM PARENT HOSTS */

		/* draw hosts in child "layers" */
		for(current_layer=1;;current_layer++){

			/* how many members in this layer? */
			layer_members=number_of_host_layer_members(this_host,current_layer);

			if(layer_members==0)
				break;

			current_layer_member=0;

			/* see which hosts are members of this layer and calculate drawing coords */
			for(temp_hostextinfo=hostextinfo_list;temp_hostextinfo!=NULL;temp_hostextinfo=temp_hostextinfo->next){

				/* find the host that matches this entry */
				temp_host=find_host(temp_hostextinfo->host_name);

				if(temp_host==NULL)
					continue;

				/* is this host a member of the current child layer? */
				if(host_child_depth_separation(this_host,temp_host)==current_layer){
					temp_hostextinfo->should_be_drawn=TRUE;
					temp_hostextinfo->have_2d_coords=TRUE;
					temp_hostextinfo->x_2d=center_x-(((layer_members*DEFAULT_NODE_WIDTH)+((layer_members-1)*DEFAULT_NODE_HSPACING))/2)+(current_layer_member*(DEFAULT_NODE_WIDTH+DEFAULT_NODE_HSPACING))+(DEFAULT_NODE_WIDTH/2);
					if(this_host==NULL)
						temp_hostextinfo->y_2d=((DEFAULT_NODE_HEIGHT+DEFAULT_NODE_VSPACING)*current_layer)+offset_y;
					else
						temp_hostextinfo->y_2d=((DEFAULT_NODE_HEIGHT+DEFAULT_NODE_VSPACING)*(current_layer+1))+offset_y;
					current_layer_member++;
				        }
			        }
		        }

	        }


	/***** "BALANCED" TREE MODE *****/
	else if(layout_method==LAYOUT_BALANCED_TREE){

		/* find the "main" host we're displaying  - DO NOT USE THIS (THIS IS THE OLD METHOD) */
		/*
		if(show_all_hosts==TRUE)
			this_host=NULL;
		else
			this_host=find_host(host_name);
		*/

		/* always use NULL as the "main" host, screen coords/dimensions are adjusted automatically */
		this_host=NULL;

		/* find total number of immediate parents for this host */
		parent_hosts=number_of_immediate_parent_hosts(this_host);

		/* find the max drawing width we have... */
		max_drawing_width=max_child_host_drawing_width(this_host);
		if(parent_hosts>max_drawing_width)
			max_drawing_width=parent_hosts;

		/* calculate center x coord */
		center_x=(((DEFAULT_NODE_WIDTH*max_drawing_width)+(DEFAULT_NODE_HSPACING*(max_drawing_width-1)))/2)+offset_x;

		/* coords for Nagios icon if necessary */
		if(this_host==NULL || this_host->parent_hosts==NULL){
			nagios_icon_x=center_x;
			nagios_icon_y=offset_y;
			draw_nagios_icon=TRUE;
		        }

		/* do we need to draw a link to parent(s)? */
		if(this_host!=NULL && is_host_immediate_child_of_host(NULL,this_host)==FALSE){
			draw_parent_links=TRUE;
			offset_y+=DEFAULT_NODE_HEIGHT+DEFAULT_NODE_VSPACING;
		        }

		/* see which hosts we should draw and calculate drawing coords */
		for(temp_hostextinfo=hostextinfo_list;temp_hostextinfo!=NULL;temp_hostextinfo=temp_hostextinfo->next){

			/* find the host that matches this entry */
			temp_host=find_host(temp_hostextinfo->host_name);

			if(temp_host==NULL)
				continue;

			/* this is an immediate parent of the "main" host we're drawing */
			else if(is_host_immediate_parent_of_host(this_host,temp_host)==TRUE){
				temp_hostextinfo->should_be_drawn=TRUE;
				temp_hostextinfo->have_2d_coords=TRUE;
				temp_hostextinfo->x_2d=center_x-(((parent_hosts*DEFAULT_NODE_WIDTH)+((parent_hosts-1)*DEFAULT_NODE_HSPACING))/2)+(current_parent_host*(DEFAULT_NODE_WIDTH+DEFAULT_NODE_HSPACING))+(DEFAULT_NODE_WIDTH/2);
				temp_hostextinfo->y_2d=offset_y;
				current_parent_host++;
			        }

			/* this is the "main" host we're drawing */
			else if(this_host==temp_host){
				temp_hostextinfo->should_be_drawn=TRUE;
				temp_hostextinfo->have_2d_coords=TRUE;
				temp_hostextinfo->x_2d=center_x;
				temp_hostextinfo->y_2d=DEFAULT_NODE_HEIGHT+DEFAULT_NODE_VSPACING+offset_y;
			        }

			/* else do not draw this host (we might if its a child - see below, but assume no for now) */
			else{
				temp_hostextinfo->should_be_drawn=FALSE;
				temp_hostextinfo->have_2d_coords=FALSE;
			        }
		        }

		/* draw all children hosts */
		calculate_balanced_tree_coords(this_host,center_x,DEFAULT_NODE_HEIGHT+DEFAULT_NODE_VSPACING+offset_y);

	        }


	/***** CIRCULAR LAYOUT MODE *****/
	else if(layout_method==LAYOUT_CIRCULAR || layout_method==LAYOUT_CIRCULAR_MARKUP || layout_method==LAYOUT_CIRCULAR_BALLOON){

		/* draw process icon */
		nagios_icon_x=0;
		nagios_icon_y=0;
		draw_nagios_icon=TRUE;

		/* calculate coordinates for all hosts */
		calculate_circular_coords();
	        }

	return;
        }



/* calculates max possible image dimensions */
void calculate_total_image_bounds(void){
	hostextinfo *temp_hostextinfo;

	total_image_width=0;
	total_image_height=0;

	/* check all extended host information entries... */
	for(temp_hostextinfo=hostextinfo_list;temp_hostextinfo!=NULL;temp_hostextinfo=temp_hostextinfo->next){

		/* only check entries that have 2-D coords specified */
		if(temp_hostextinfo->have_2d_coords==FALSE)
			continue;

		/* skip hosts we shouldn't be drawing */
		if(temp_hostextinfo->should_be_drawn==FALSE)
			continue;

		if(temp_hostextinfo->x_2d>total_image_width)
			total_image_width=temp_hostextinfo->x_2d;
		if(temp_hostextinfo->y_2d>total_image_height)
			total_image_height=temp_hostextinfo->y_2d;

		coordinates_were_specified=TRUE;
	        }

	/* add some space for icon size and overlapping text... */
	if(coordinates_were_specified==TRUE){

		total_image_width+=(DEFAULT_NODE_WIDTH*2);
		total_image_height+=DEFAULT_NODE_HEIGHT;

		/* add space for bottom margin if necessary */
		total_image_height+=bottom_margin;
	        }

	/* image size should be at least as large as dimensions of background image */
	if(total_image_width<background_image_width)
		total_image_width=background_image_width;
	if(total_image_height<background_image_height)
		total_image_height=background_image_height;

	/* we didn't find any hosts that had user-supplied coordinates, so we're going to display a warning */
	if(coordinates_were_specified==FALSE){
		coordinates_were_specified=FALSE;
		total_image_width=COORDS_WARNING_WIDTH;
		total_image_height=COORDS_WARNING_HEIGHT;
	        }

	return;
        }


/* calculates canvas coordinates/dimensions */
void calculate_canvas_bounds(void){

	if(user_supplied_canvas==FALSE && strcmp(host_name,"all"))
		calculate_canvas_bounds_from_host(host_name);

	/* calculate canvas origin (based on total image bounds) */
	if(canvas_x<=0 || canvas_width>total_image_width)
		canvas_x=0;
	if(canvas_y<=0 || canvas_height>total_image_height)
		canvas_y=0;

	/* calculate canvas dimensions */
	if(canvas_height<=0)
		canvas_height=(total_image_height-canvas_y);
	if(canvas_width<=0)
		canvas_width=(total_image_width-canvas_x);

	if(canvas_x+canvas_width>total_image_width)
		canvas_width=total_image_width-canvas_x;
	if(canvas_y+canvas_height>total_image_height)
		canvas_height=total_image_height-canvas_y;

	return;
        }


/* calculates canvas coordinates/dimensions around a particular host */
void calculate_canvas_bounds_from_host(char *host_name){
	hostextinfo *temp_hostextinfo;
	int zoom_width;
	int zoom_height;

	/* find the extended host info */
	temp_hostextinfo=find_hostextinfo(host_name);
	if(temp_hostextinfo==NULL)
		return;

	/* make sure we have 2-D coords */
	if(temp_hostextinfo->have_2d_coords==FALSE)
		return;

	if(max_image_width>0 && proximity_width>max_image_width)
		zoom_width=max_image_width;
	else
		zoom_width=proximity_width;
	if(max_image_height>0 && proximity_height>max_image_height)
		zoom_height=max_image_height;
	else
		zoom_height=proximity_height;

	canvas_width=zoom_width;
	if(canvas_width>=total_image_width)
		canvas_x=0;
	else
		canvas_x=(temp_hostextinfo->x_2d-(zoom_width/2));

	canvas_height=zoom_height;
	if(canvas_height>=total_image_height)
		canvas_y=0;
	else
		canvas_y=(temp_hostextinfo->y_2d-(zoom_height/2));


	return;
        }


/* calculates scaling factor used in image generation */
void calculate_scaling_factor(void){
	double x_scaling=1.0;
	double y_scaling=1.0;

	/* calculate horizontal scaling factor */
	if(max_image_width<=0 || canvas_width<=max_image_width)
		x_scaling=1.0;
	else
		x_scaling=(double)((double)max_image_width/(double)canvas_width);

	/* calculate vertical scaling factor */
	if(max_image_height<=0 || canvas_height<=max_image_height)
		y_scaling=1.0;
	else
		y_scaling=(double)((double)max_image_height/(double)canvas_height);

	/* calculate general scaling factor to use */
	if(x_scaling<y_scaling)
		scaling_factor=x_scaling;
	else
		scaling_factor=y_scaling;

	/*** USER-SUPPLIED SCALING FACTOR ***/
	if(user_supplied_scaling==TRUE)
		scaling_factor=user_scaling_factor;

	return;
        }


/* finds hosts that can be drawn in the canvas area */
void find_eligible_hosts(void){
	hostextinfo *temp_hostextinfo;
	int total_eligible_hosts=0;
	host *temp_host;

	/* check all extended host information entries... */
	for(temp_hostextinfo=hostextinfo_list;temp_hostextinfo!=NULL;temp_hostextinfo=temp_hostextinfo->next){

		/* find the host */
		temp_host=find_host(temp_hostextinfo->host_name);

		if(temp_host==NULL)
			temp_hostextinfo->should_be_drawn=FALSE;

		/* only include hosts that have 2-D coords supplied */
		else if(temp_hostextinfo->have_2d_coords==FALSE)
			temp_hostextinfo->should_be_drawn=FALSE;

		/* make sure coords are all positive */
		else if(temp_hostextinfo->x_2d<0 || temp_hostextinfo->y_2d<0)
			temp_hostextinfo->should_be_drawn=FALSE;

		/* make sure x coordinates fall within canvas bounds */
		else if(temp_hostextinfo->x_2d<(canvas_x-DEFAULT_NODE_WIDTH) || temp_hostextinfo->x_2d>(canvas_x+canvas_width))
			temp_hostextinfo->should_be_drawn=FALSE;

		/* make sure y coordinates fall within canvas bounds */
		else if(temp_hostextinfo->y_2d<(canvas_y-DEFAULT_NODE_HEIGHT) || temp_hostextinfo->y_2d>(canvas_y+canvas_height))
			temp_hostextinfo->should_be_drawn=FALSE;

		/* see if the user is authorized to view the host */
		else if(is_authorized_for_host(temp_host,&current_authdata)==FALSE)
			temp_hostextinfo->should_be_drawn=FALSE;

		/* all checks passed, so we can draw the host! */
		else{
			temp_hostextinfo->should_be_drawn=TRUE;
			total_eligible_hosts++;
		        }
	        }

	return;
        }



/******************************************************************/
/*********************** DRAWING FUNCTIONS ************************/
/******************************************************************/


/* loads background image from file */
void load_background_image(void){
	char temp_buffer[MAX_INPUT_BUFFER];

	/* bail out if we shouldn't be drawing a background image */
	if(layout_method!=LAYOUT_USER_SUPPLIED || statusmap_background_image==NULL)
		return;

	snprintf(temp_buffer,sizeof(temp_buffer)-1,"%s%s",physical_images_path,statusmap_background_image);
	temp_buffer[sizeof(temp_buffer)-1]='\x0';

	/* read the background image into memory */
	background_image=load_image_from_file(temp_buffer);

	/* grab background image dimensions for calculating total image width later */
	if(background_image!=NULL){
		background_image_width=background_image->sx;
		background_image_height=background_image->sy;
	        }

	/* if we are just creating the html, we don't need the image anymore */
	if(create_type==CREATE_HTML && background_image!=NULL)
		gdImageDestroy(background_image);

	return;
	}


/* draws background image on drawing canvas */
void draw_background_image(void){

	/* bail out if we shouldn't be drawing a background image */
	if(create_type==CREATE_HTML || layout_method!=LAYOUT_USER_SUPPLIED || statusmap_background_image==NULL)
		return;
	if( 2 != statusmap_mod ) {
		/* bail out if we don't have an image */
		if(background_image==NULL)
			return;
		/* copy the background image to the canvas */
		gdImageCopy(map_image,background_image,0,0,canvas_x,canvas_y,canvas_width,canvas_height);
		/* free memory for background image, as we don't need it anymore */
		gdImageDestroy(background_image);
	}
	else {
		char temp_buffer[MAX_INPUT_BUFFER];
		memset(temp_buffer,'\x0',MAX_INPUT_BUFFER);
		snprintf(temp_buffer,sizeof(temp_buffer)-1,"%s%s",url_images_path,statusmap_background_image);
		temp_buffer[sizeof(temp_buffer)-1]='\x0';
		fprintf(image_output_file,"<g id=\"Background_Layer\">\n");
		fprintf(image_output_file,"<image id=\"BackgroundIMG\" x=\"0\" y=\"0\" width=\"%d\" height=\"%d\" xlink:href=\"%s\"/>\n",
			canvas_width,canvas_height,temp_buffer);
		fprintf(image_output_file,"</g>\n");
	}
	return;
}



/* draws background "extras" */
void draw_background_extras(void){
	/* bail out if we shouldn't be here */
	if(create_type==CREATE_HTML)
		return;
	if( 2 != statusmap_mod ) {
	 	/* circular layout stuff... */
		if(layout_method==LAYOUT_CIRCULAR_MARKUP){
			/* draw colored sections... */
			draw_circular_markup();
	        }
	}else{
		/*sorry i don't know how to draw it. :( */
	}
	return;
        }


/* draws host links */
void draw_host_links(void){
	hostextinfo *temp_hostextinfo;
	hostextinfo *temp_parent_hostextinfo;
	host *this_host;
	host *main_host;
	host *parent_host;
	hostsmember *temp_hostsmember;
	int status_color=color_black;
	hoststatus *this_hoststatus;
	hoststatus *parent_hoststatus;
	int child_in_layer_list=FALSE;
	int parent_in_layer_list=FALSE;
	int dotted_line=FALSE;
	int x=0;
	int y=0;

	if(create_type==CREATE_HTML)
		return;

	if(use_links==FALSE)
		return;
	if( 2 == statusmap_mod )	printf("<g id=\"Links_Layer\">\n");

	/* find the "main" host we're drawing */
	main_host=find_host(host_name);
	if(show_all_hosts==TRUE)
		main_host=NULL;

	/* check all extended host information entries... */
	for(temp_hostextinfo=hostextinfo_list;temp_hostextinfo!=NULL;temp_hostextinfo=temp_hostextinfo->next){

		/* find the config entry for this host */
		this_host=find_host(temp_hostextinfo->host_name);
		if(this_host==NULL)
			continue;

		/* only draw link if user is authorized to view this host */
		if(is_authorized_for_host(this_host,&current_authdata)==FALSE)
			continue;

		/* this is a "root" host, so draw link to Nagios process icon if using auto-layout mode */
		if(this_host->parent_hosts==NULL && layout_method!=LAYOUT_USER_SUPPLIED && draw_nagios_icon==TRUE){

			x=temp_hostextinfo->x_2d+(DEFAULT_NODE_WIDTH/2)-canvas_x;
			y=temp_hostextinfo->y_2d+(DEFAULT_NODE_WIDTH/2)-canvas_y;

			draw_line(x,y,nagios_icon_x+(DEFAULT_NODE_WIDTH/2)-canvas_x,nagios_icon_y+(DEFAULT_NODE_WIDTH/2)-canvas_y,color_black);
		        }

		/* this is a child of the main host we're drawing in auto-layout mode... */
		if(layout_method!=LAYOUT_USER_SUPPLIED && draw_child_links==TRUE && number_of_immediate_child_hosts(this_host)>0 && is_host_immediate_child_of_host(main_host,this_host)==TRUE){
			/* determine color to use when drawing links to children  */
			this_hoststatus=find_hoststatus(temp_hostextinfo->host_name);
			if(this_hoststatus!=NULL){
				if(this_hoststatus->status==HOST_DOWN || this_hoststatus->status==HOST_UNREACHABLE)
					status_color=color_red;
				else
					status_color=color_black;
		                }
			else
				status_color=color_black;

			x=temp_hostextinfo->x_2d+(DEFAULT_NODE_WIDTH/2)-canvas_x;
			y=(temp_hostextinfo->y_2d+(DEFAULT_NODE_WIDTH)/2)-canvas_y;

			draw_dashed_line(x,y,x,y+DEFAULT_NODE_HEIGHT+DEFAULT_NODE_VSPACING,status_color);

			/* draw arrow tips */
			draw_line(x,y+DEFAULT_NODE_HEIGHT+DEFAULT_NODE_VSPACING,x-5,y+DEFAULT_NODE_HEIGHT+DEFAULT_NODE_VSPACING-5,color_black);
			draw_line(x,y+DEFAULT_NODE_HEIGHT+DEFAULT_NODE_VSPACING,x+5,y+DEFAULT_NODE_HEIGHT+DEFAULT_NODE_VSPACING-5,color_black);
		        }

		/* this is a parent of the main host we're drawing in auto-layout mode... */
		if(layout_method!=LAYOUT_USER_SUPPLIED && draw_parent_links==TRUE && is_host_immediate_child_of_host(this_host,main_host)==TRUE){

			x=temp_hostextinfo->x_2d+(DEFAULT_NODE_WIDTH/2)-canvas_x;
			y=temp_hostextinfo->y_2d+(DEFAULT_NODE_WIDTH/2)-canvas_y;

			draw_dashed_line(x,y,x,y-DEFAULT_NODE_HEIGHT-DEFAULT_NODE_VSPACING,color_black);

			/* draw arrow tips */
			draw_line(x,y-DEFAULT_NODE_HEIGHT-DEFAULT_NODE_VSPACING,x-5,y-DEFAULT_NODE_HEIGHT-DEFAULT_NODE_VSPACING+5,color_black);
			draw_line(x,y-DEFAULT_NODE_HEIGHT-DEFAULT_NODE_VSPACING,x+5,y-DEFAULT_NODE_HEIGHT-DEFAULT_NODE_VSPACING+5,color_black);
		        }

		/* draw links to all parent hosts */
		for(temp_hostsmember=this_host->parent_hosts;temp_hostsmember!=NULL;temp_hostsmember=temp_hostsmember->next){

			/* find extended info entry for this parent host */
			temp_parent_hostextinfo=find_hostextinfo(temp_hostsmember->host_name);
			if(temp_parent_hostextinfo==NULL)
				continue;

			/* don't draw the link if we don't have the coords */
			if(temp_parent_hostextinfo->have_2d_coords==FALSE || temp_hostextinfo->have_2d_coords==FALSE)
				continue;

			/* find the parent host config entry */
			parent_host=find_host(temp_parent_hostextinfo->host_name);
			if(parent_host==NULL)
				continue;

			/* only draw link if user is authorized for this parent host */
			if(is_authorized_for_host(parent_host,&current_authdata)==FALSE)
				continue;

			/* are the hosts in the layer list? */
			child_in_layer_list=is_host_in_layer_list(this_host);
			parent_in_layer_list=is_host_in_layer_list(parent_host);

			/* use dotted or solid line? */
			/* either the child or parent should not be drawn, so use a dotted line */
			if((child_in_layer_list==TRUE && parent_in_layer_list==FALSE) || (child_in_layer_list==FALSE && parent_in_layer_list==TRUE))
				dotted_line=TRUE;
			/* both hosts should not be drawn, so use a dotted line */
			else if((child_in_layer_list==FALSE && parent_in_layer_list==FALSE && exclude_layers==FALSE) || (child_in_layer_list==TRUE && parent_in_layer_list==TRUE && exclude_layers==TRUE))
				dotted_line=TRUE;
			/* both hosts should be drawn, so use a solid line */
			else
				dotted_line=FALSE;

			/* determine color to use when drawing links to parent host */
			parent_hoststatus=find_hoststatus(temp_parent_hostextinfo->host_name);
			if(parent_hoststatus!=NULL){
				if(parent_hoststatus->status==HOST_DOWN || parent_hoststatus->status==HOST_UNREACHABLE)
					status_color=color_red;
				else
					status_color=color_black;
		                }
			else
				status_color=color_black;

			/* draw the link */
			if(dotted_line==TRUE)
				draw_dotted_line((temp_hostextinfo->x_2d+(DEFAULT_NODE_WIDTH/2))-canvas_x,(temp_hostextinfo->y_2d+(DEFAULT_NODE_WIDTH)/2)-canvas_y,(temp_parent_hostextinfo->x_2d+(DEFAULT_NODE_WIDTH/2))-canvas_x,(temp_parent_hostextinfo->y_2d+(DEFAULT_NODE_WIDTH/2))-canvas_y,status_color);
			else
				draw_line((temp_hostextinfo->x_2d+(DEFAULT_NODE_WIDTH/2))-canvas_x,(temp_hostextinfo->y_2d+(DEFAULT_NODE_WIDTH)/2)-canvas_y,(temp_parent_hostextinfo->x_2d+(DEFAULT_NODE_WIDTH/2))-canvas_x,(temp_parent_hostextinfo->y_2d+(DEFAULT_NODE_WIDTH/2))-canvas_y,status_color);
		        }

	        }
	if( 2 == statusmap_mod )	printf("</g>\n");
	return;
        }



/* draws hosts */
void draw_hosts(void){
	hostextinfo *temp_hostextinfo;
	host *temp_host;
	int x1, x2;
	int y1, y2;
	int has_image=FALSE;
	char image_input_file[MAX_INPUT_BUFFER];
	int current_radius=0;
	int status_color=color_black;
	hoststatus *temp_hoststatus;
	int in_layer_list=FALSE;
	int average_host_services;
	int host_services;
	double host_services_ratio;
	int outer_radius;
	int inner_radius;
	int time_color=0;
	time_t current_time;
	int translated_x;
	int translated_y;
	if( (2 == statusmap_mod)&&(CREATE_IMAGE==create_type) )printf("<g id=\"Hosts_Layer\">\n");
	/* user didn't supply any coordinates for hosts, so display a warning */
	if(coordinates_were_specified==FALSE){
		if(create_type==CREATE_IMAGE){
			draw_text("You have not supplied any host drawing coordinates, so you cannot use this layout method.",(COORDS_WARNING_WIDTH/2),30,color_black);
			draw_text("Read the FAQs for more information on specifying drawing coordinates or select a different layout method.",(COORDS_WARNING_WIDTH/2),45,color_black);
        }
		return;
    }
	/* draw Nagios process icon if using auto-layout mode */
	if(layout_method!=LAYOUT_USER_SUPPLIED && draw_nagios_icon==TRUE){
		/* get coords of bounding box */
		x1=nagios_icon_x-canvas_x;
		x2=x1+DEFAULT_NODE_WIDTH;
		y1=nagios_icon_y-canvas_y;
		y2=y1+DEFAULT_NODE_HEIGHT;
		/* get the name of the image file to open for the logo */
		snprintf(image_input_file,sizeof(image_input_file)-1,"%s%s",physical_logo_images_path,NAGIOS_GD2_ICON);
		image_input_file[sizeof(image_input_file)-1]='\x0';
		if(2 != statusmap_mod){
			/* read in the image from file... */
			logo_image=load_image_from_file(image_input_file);
        	/* copy the logo image to the canvas image... */
			if(logo_image!=NULL){
				gdImageCopy(map_image,logo_image,x1,y1,0,0,logo_image->sx,logo_image->sy);
				gdImageDestroy(logo_image);
        	}
			/* if we don't have an image, draw a bounding box */
			else{
				draw_line(x1,y1,x1,y1+DEFAULT_NODE_WIDTH,color_black);
				draw_line(x1,y1+DEFAULT_NODE_WIDTH,x2,y1+DEFAULT_NODE_WIDTH,color_black);
				draw_line(x2,y1+DEFAULT_NODE_WIDTH,x2,y1,color_black);
				draw_line(x2,y1,x1,y1,color_black);
        	}
		}
		else{
			fprintf(image_output_file,"<image id=\"IMG_%s\" x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" xlink:href=\"%s%s\"/>\n",
				temp_hostextinfo->host_name,x1,y1-scaled_image_height/4,scaled_image_width,scaled_image_height,url_logo_images_path,temp_hostextinfo->icon_image);
		}
		if(create_type==CREATE_IMAGE)
			draw_text("Nagios Process",x1+(DEFAULT_NODE_WIDTH/2),y1+DEFAULT_NODE_HEIGHT,color_black);
    }//if auto draw
	/* calculate average services per host */
	average_host_services=4;
	/* draw all hosts... */
	for(temp_hostextinfo=hostextinfo_list;temp_hostextinfo!=NULL;temp_hostextinfo=temp_hostextinfo->next){
		/* skip hosts that should not be drawn */
		if(temp_hostextinfo->should_be_drawn==FALSE)
			continue;
		/* find the host */
		temp_host=find_host(temp_hostextinfo->host_name);
		/* is this host in the layer inclusion/exclusion list? */
		in_layer_list=is_host_in_layer_list(temp_host);
		if((in_layer_list==TRUE && exclude_layers==TRUE) || (in_layer_list==FALSE && exclude_layers==FALSE))
			continue;
		/* get coords of host bounding box */
		x1=temp_hostextinfo->x_2d-canvas_x;
		x2=x1+DEFAULT_NODE_WIDTH;
		y1=temp_hostextinfo->y_2d-canvas_y;
		y2=y1+DEFAULT_NODE_HEIGHT;
		if(create_type==CREATE_IMAGE){
			temp_hoststatus=find_hoststatus(temp_hostextinfo->host_name);
			if(temp_hoststatus!=NULL){
				if(temp_hoststatus->status==HOST_DOWN)
					status_color=color_red;
				else if(temp_hoststatus->status==HOST_UNREACHABLE)
						status_color=color_red;
				else if(temp_hoststatus->status==HOST_UP)
						status_color=color_green;
				else if(temp_hoststatus->status==HOST_PENDING)
						status_color=color_grey;
		    } else
				status_color=color_black;
			/* use balloons instead of icons... */
			if(layout_method==LAYOUT_CIRCULAR_BALLOON){
			/* get the number of services associated with the host */
			host_services=number_of_host_services(temp_host);
			if(average_host_services==0){
				host_services_ratio=0.0;
			} else {
				host_services_ratio=(double)((double)host_services/(double)average_host_services);
			}
			/* calculate size of node */
			if(host_services_ratio>=2.0)
				outer_radius=DEFAULT_NODE_WIDTH;
			else if(host_services_ratio>=1.5)
				outer_radius=DEFAULT_NODE_WIDTH*0.8;
			else if(host_services_ratio>=1.0)
				outer_radius=DEFAULT_NODE_WIDTH*0.6;
			else if(host_services_ratio>=0.5)
				outer_radius=DEFAULT_NODE_WIDTH*0.4;
			else
				outer_radius=DEFAULT_NODE_WIDTH*0.2;
			/* calculate width of border */
			if(temp_hoststatus==NULL)
				inner_radius=outer_radius;
			else if((temp_hoststatus->status==HOST_DOWN || temp_hoststatus->status==HOST_UNREACHABLE) && temp_hoststatus->problem_has_been_acknowledged==FALSE)
				inner_radius=outer_radius-3;
			else
				inner_radius=outer_radius;
			/* fill node with color based on how long its been in this state... */
			gdImageArc(map_image,x1+(DEFAULT_NODE_WIDTH/2),y1+(DEFAULT_NODE_WIDTH/2),outer_radius,outer_radius,0,360,color_blue);
			/* determine fill color */
			time(&current_time);
			if(temp_hoststatus==NULL)
				time_color=color_white;
			else if(current_time-temp_hoststatus->last_state_change<=900)
				time_color=color_orange;
			else if(current_time-temp_hoststatus->last_state_change<=3600)
				time_color=color_yellow;
			else
				time_color=color_white;
			/* fill node with appropriate time color */
			/* the fill function only works with coordinates that are in bounds of the actual image */
			translated_x=x1+(DEFAULT_NODE_WIDTH/2);
			translated_y=y1+(DEFAULT_NODE_WIDTH/2);
			if(translated_x>0 && translated_y>0 && translated_x<canvas_width && translated_y<canvas_height)
				gdImageFillToBorder(map_image,translated_x,translated_y,color_blue,time_color);
			/* border of node should reflect current state */
			for(current_radius=outer_radius;current_radius>=inner_radius;current_radius--)
				gdImageArc(map_image,x1+(DEFAULT_NODE_WIDTH/2),y1+(DEFAULT_NODE_WIDTH/2),current_radius,current_radius,0,360,status_color);
			/* draw circles around the selected host (if there is one) */
			if(!strcmp(host_name,temp_hostextinfo->host_name) && use_highlights==TRUE){
				for(current_radius=DEFAULT_NODE_WIDTH*2;current_radius>0;current_radius-=10)
					gdImageArc(map_image,x1+(DEFAULT_NODE_WIDTH/2),y1+(DEFAULT_NODE_WIDTH/2),current_radius,current_radius,0,360,status_color);
		    	}
			}
			/* normal method is to use icons for hosts... */
			else{
				/* draw a target around root hosts (hosts with no parents) */
				if(temp_host!=NULL && use_highlights==TRUE){
					if(temp_host->parent_hosts==NULL){
						if( 2 != statusmap_mod )
							gdImageArc(map_image,x1+(DEFAULT_NODE_WIDTH/2),y1+(DEFAULT_NODE_WIDTH/2),(DEFAULT_NODE_WIDTH*2),(DEFAULT_NODE_WIDTH*2),0,360,status_color);
						draw_line(x1-(DEFAULT_NODE_WIDTH/2),y1+(DEFAULT_NODE_WIDTH/2),x1+(DEFAULT_NODE_WIDTH*3/2),y1+(DEFAULT_NODE_WIDTH/2),status_color);
						draw_line(x1+(DEFAULT_NODE_WIDTH/2),y1-(DEFAULT_NODE_WIDTH/2),x1+(DEFAULT_NODE_WIDTH/2),y1+(DEFAULT_NODE_WIDTH*3/2),status_color);
	    	        }
		    	}//if(temp_host!=NULL)
				/* draw circles around the selected host (if there is one) */
				if(!strcmp(host_name,temp_hostextinfo->host_name) && use_highlights==TRUE){
					for(current_radius=DEFAULT_NODE_WIDTH*2;current_radius>0;current_radius-=10)
						if( 2 != statusmap_mod ) gdImageArc(map_image,x1+(DEFAULT_NODE_WIDTH/2),y1+(DEFAULT_NODE_WIDTH/2),current_radius,current_radius,0,360,status_color);
			    }
				if(temp_hostextinfo->statusmap_image!=NULL)
					has_image=TRUE;
				else
					has_image=FALSE;
				/* load the logo associated with this host */
				if(has_image==TRUE){
			        /* get the name of the image file to open for the logo */
					snprintf(image_input_file,sizeof(image_input_file)-1,"%s%s",physical_logo_images_path,temp_hostextinfo->statusmap_image);
					image_input_file[sizeof(image_input_file)-1]='\x0';
        	        /* copy the logo image to the canvas image... */
					if( 2 == statusmap_mod){
						fprintf(image_output_file,"<image id=\"IMG_%s\" x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" xlink:href=\"%s%s\"/>\n",
							temp_hostextinfo->host_name,x1,y1-scaled_image_height/4,scaled_image_width,scaled_image_height,url_logo_images_path,temp_hostextinfo->icon_image);
					}
					else{
				        /* read in the logo image from file... */
						logo_image=load_image_from_file(image_input_file);
						if(logo_image!=NULL){
							gdImageCopy(map_image,logo_image,x1,y1,0,0,logo_image->sx,logo_image->sy);
							gdImageDestroy(logo_image);
						}
						else
							has_image=FALSE;
                	}
            	}
				/* if the host doesn't have an image associated with it (or the user doesn't have rights to see this host), use the unknown image */
				if(has_image==FALSE){
					if(unknown_logo_image!=NULL){
						if( 2 == statusmap_mod){
							fprintf(image_output_file,"<image id=\"IMG_%s\" x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" xlink:href=\"%s%s\"/>\n",
								"UNKNOW",x1,y1,scaled_image_width,scaled_image_height,url_logo_images_path,UNKNOWN_ICON_IMAGE);
						}
						else
							gdImageCopy(map_image,unknown_logo_image,x1,y1,0,0,unknown_logo_image->sx,unknown_logo_image->sy);
					}
					else{
						/* last ditch effort - draw a host bounding box */
						draw_line(x1,y1,x1,y1+DEFAULT_NODE_WIDTH,color_black);
						draw_line(x1,y1+DEFAULT_NODE_WIDTH,x2,y1+DEFAULT_NODE_WIDTH,color_black);
						draw_line(x2,y1+DEFAULT_NODE_WIDTH,x2,y1,color_black);
						draw_line(x2,y1,x1,y1,color_black);
					}
	    		}
    		}//else
			/* draw host name, status, etc. */
		draw_host_text(temp_hostextinfo->host_name,x1+(DEFAULT_NODE_WIDTH/2),y1+DEFAULT_NODE_HEIGHT);
		}//if(create_type==CREATE_IMAGE)
		/* we're creating HTML image map... */
		else{
			printf("<AREA shape='rect' ");
			/* coordinates */
			printf("coords='%d,%d,%d,%d' ",(int)(x1*scaling_factor),(int)(y1*scaling_factor),(int)((x1+DEFAULT_NODE_WIDTH)*scaling_factor),(int)((y1+DEFAULT_NODE_HEIGHT)*scaling_factor));
			/* URL */
			if(!strcmp(host_name,temp_hostextinfo->host_name))
				printf("href='%s?host=%s' ",STATUS_CGI,url_encode(temp_hostextinfo->host_name));
			else{
				printf("href='%s?host=%s&layout=%d&max_width=%d&max_height=%d&proximity_width=%d&proximity_height=%d%s%s%s%s%s",STATUSMAP_CGI,url_encode(temp_hostextinfo->host_name),layout_method,max_image_width,max_image_height,proximity_width,proximity_height,(display_header==TRUE)?"":"&noheader",(use_links==FALSE)?"&nolinks":"",(use_text==FALSE)?"&notext":"",(use_highlights==FALSE)?"&nohighlights":"",(display_popups==FALSE)?"&nopopups":"");
				if(user_supplied_scaling==TRUE)
					printf("&scaling_factor=%2.1f",user_scaling_factor);
				print_layer_url(TRUE);
				printf("' ");
        	}
			/* popup text */
			if(display_popups==TRUE){
				printf("onMouseOver='showPopup(\"");
				write_host_popup_text(find_host(temp_hostextinfo->host_name));
				printf("\",event)' onMouseOut='hidePopup()'");
			}
			printf(">\n");
			}//else
		}//for
	if((2 == statusmap_mod)&&(CREATE_IMAGE==create_type))printf("</g>\n");
	return;
}


/* draws text */
void draw_text(char *buffer,int x,int y,int text_color){
	if( 2 == statusmap_mod){
		int string_width=gdFontSmall->w*(strlen(buffer)-2);
		int string_height=gdFontSmall->h;
		printf("<text id=\"t_%s\" x=\"%d\" y=\"%d\" font-size=\"%d\" stroke=\"#%06x\" fill=\"#%06x\">%s</text>\n",
		  buffer,(x-string_width/2),y-(string_height),gdFontSmall->h,text_color, text_color, buffer);
	}else{
		int string_width=0;
		int string_height=0;
		int brect[8];
		/* write the string to the generated image... */
		string_height=gdFontSmall->h;
		string_width=gdFontSmall->w*strlen(buffer);
		if(layout_method!=LAYOUT_CIRCULAR_MARKUP)
			gdImageFilledRectangle(map_image,x-(string_width/2)-2,y-(2*string_height),x+(string_width/2)+2,y-string_height,color_white);
		gdImageStringTTF(map_image,&brect[0],text_color, ttf_file,10,0.0, x-(string_width/2),y-(string_height),buffer);
	}
	return;
        }


/* draws host text */
/* 注释：这段是为了补Nagios调用GD库中的问题而加入的 */
void draw_host_text(char *name,int x,int y){
	hoststatus *temp_hoststatus;
	int status_color=color_black;
	char temp_buffer[MAX_INPUT_BUFFER];

	if(use_text==FALSE)
		return;
	strncpy(temp_buffer,name,sizeof(temp_buffer)-1);
	temp_buffer[sizeof(temp_buffer)-1]='\x0';

	/* write the host status string to the generated image... */
	draw_text(temp_buffer,x,y,color_black);

	/* find the status entry for this host */
	temp_hoststatus=find_hoststatus(name);

	/* get the status of the host (pending, up, down, or unreachable) */
	if(temp_hoststatus!=NULL){

		/* draw the status string */
		if(temp_hoststatus->status==HOST_DOWN){
			strncpy(temp_buffer,"宕机",sizeof(temp_buffer));
			status_color=color_red;
                        }
		else if(temp_hoststatus->status==HOST_UNREACHABLE){
			strncpy(temp_buffer,"不可达",sizeof(temp_buffer));
			status_color=color_red;
                        }
		else if(temp_hoststatus->status==HOST_UP){
			strncpy(temp_buffer,"运行",sizeof(temp_buffer));
			status_color=color_green;
                        }
		else if(temp_hoststatus->status==HOST_PENDING){
			strncpy(temp_buffer,"未决",sizeof(temp_buffer));
			status_color=color_grey;
                        }
		else{
			strncpy(temp_buffer,"未知",sizeof(temp_buffer));
			status_color=color_orange;
	                }

		temp_buffer[sizeof(temp_buffer)-1]='\x0';

		/* write the host status string to the generated image... */
		draw_text(temp_buffer,x,y+gdFontSmall->h,status_color);
                }

	return;
        }


/* writes popup text for a specific host */
void write_host_popup_text(host *hst){
	hostextinfo *temp_hostextinfo;
	hoststatus *temp_status;
	hostsmember *temp_hostsmember;
	int service_totals;
	char date_time[48];
	time_t current_time;
	time_t t;
	char state_duration[48];
	int days;
	int hours;
	int minutes;
	int seconds;

	if(hst==NULL){
		printf("主机数据不存在。");
		return;
	        }

	/* find the status entry for this host */
	temp_status=find_hoststatus(hst->name);
	if(temp_status==NULL){
		printf("主机的状态信息不存在。");
		return;
	        }

	/* strip nasty stuff from plugin output */
	sanitize_plugin_output(temp_status->plugin_output);

	printf("<table border=0 cellpadding=0 cellspacing=5>");

	temp_hostextinfo=find_hostextinfo(hst->name);
	if(temp_hostextinfo!=NULL){
		printf("<tr><td><img src=%s%s border=0 width=40 height=40></td>",url_logo_images_path,(temp_hostextinfo->icon_image==NULL)?UNKNOWN_ICON_IMAGE:temp_hostextinfo->icon_image);
		printf("<td class=\\\"popupText\\\"><i>%s</i></td></tr>",(temp_hostextinfo->icon_image_alt==NULL)?"":html_encode(temp_hostextinfo->icon_image_alt));
	        }

	printf("<tr><td class=\\\"popupText\\\">名称:</td><td class=\\\"popupText\\\"><b>%s</b></td></tr>",html_encode(hst->name));
	printf("<tr><td class=\\\"popupText\\\">别名:</td><td class=\\\"popupText\\\"><b>%s</b></td></tr>",html_encode(hst->alias));
	printf("<tr><td class=\\\"popupText\\\">地址:</td><td class=\\\"popupText\\\"><b>%s</b></td></tr>",html_encode(hst->address));
	printf("<tr><td class=\\\"popupText\\\">状态:</td><td class=\\\"popupText\\\"><b>");

	/* get the status of the host (pending, up, down, or unreachable) */
	if(temp_status->status==HOST_DOWN){
		printf("<font color=red>宕机状态");
		if(temp_status->problem_has_been_acknowledged==TRUE)
			printf(" (问题确认)");
		printf("</font>");
	        }

	else if(temp_status->status==HOST_UNREACHABLE){
		printf("<font color=red>不可达态");
		if(temp_status->problem_has_been_acknowledged==TRUE)
			printf(" (问题确认)");
		printf("</font>");
	        }

	else if(temp_status->status==HOST_UP)
		printf("<font color=green>运行状态</font>");

	else if(temp_status->status==HOST_PENDING)
		printf("未决状态");

	printf("</b></td></tr>");
	printf("<tr><td class=\\\"popupText\\\">状态信息:</td><td class=\\\"popupText\\\"><b>%s</b></td></tr>",(temp_status->plugin_output==NULL)?"":temp_status->plugin_output);

	current_time=time(NULL);
	if(temp_status->last_state_change==(time_t)0)
		t=current_time-program_start;
	else
		t=current_time-temp_status->last_state_change;
	get_time_breakdown((unsigned long)t,&days,&hours,&minutes,&seconds);
	snprintf(state_duration,sizeof(state_duration)-1,"%2d日%2d时%2d分%2d秒",days,hours,minutes,seconds,(temp_status->last_state_change==(time_t)0)?"+":"");
	state_duration[sizeof(state_duration)-1]='\x0';
	printf("<tr><td class=\\\"popupText\\\">持续时间:</td><td class=\\\"popupText\\\"><b>%s</b></td></tr>",state_duration);

	get_time_string(&temp_status->last_check,date_time,(int)sizeof(date_time),SHORT_DATE_TIME);
	printf("<tr><td class=\\\"popupText\\\">最近检查时间:</td><td class=\\\"popupText\\\"><b>%s</b></td></tr>",(temp_status->last_check==(time_t)0)?"未知":date_time);
	get_time_string(&temp_status->last_state_change,date_time,(int)sizeof(date_time),SHORT_DATE_TIME);
	printf("<tr><td class=\\\"popupText\\\">最近状态改变时间:</td><td class=\\\"popupText\\\"><b>%s</b></td></tr>",(temp_status->last_state_change==(time_t)0)?"未知":date_time);

	printf("<tr><td class=\\\"popupText\\\">上级节点主机:</td><td class=\\\"popupText\\\"><b>");
	if(hst->parent_hosts==NULL)
		printf("无(该主机是根节点)");
	else{
		for(temp_hostsmember=hst->parent_hosts;temp_hostsmember!=NULL;temp_hostsmember=temp_hostsmember->next)
			printf("%s%s",(temp_hostsmember==hst->parent_hosts)?"":", ",html_encode(temp_hostsmember->host_name));
	        }
	printf("</b></td></tr>");

	printf("<tr><td class=\\\"popupText\\\">下级节点主机:</td><td class=\\\"popupText\\\"><b>");
	printf("%d",number_of_immediate_child_hosts(hst));
	printf("</b></td></tr>");

	printf("</table>");

	printf("<br><b><u>Services:</u></b><br>");

	service_totals=get_servicestatus_count(hst->name,SERVICE_OK);
	if(service_totals>0)
		printf("- <font color=green>正常状态: %d <br>(OK)</font><br>",service_totals);
	service_totals=get_servicestatus_count(hst->name,SERVICE_CRITICAL);
	if(service_totals>0)
		printf("- <font color=red>紧急状态: %d <br>(CRITICAL)</font><br>",service_totals);
	service_totals=get_servicestatus_count(hst->name,SERVICE_WARNING);
	if(service_totals>0)
		printf("- <font color=orange>告警状态: %d <br>(WARNING)</font><br>",service_totals);
	service_totals=get_servicestatus_count(hst->name,SERVICE_UNKNOWN);
	if(service_totals>0)
		printf("- <font color=orange>未知状态: %d <br>(UNKNOWN)</font><br>",service_totals);
	service_totals=get_servicestatus_count(hst->name,SERVICE_PENDING);
	if(service_totals>0)
		printf("- %d pending<br>",service_totals);

	return;
        }



/* draws a solid line */
void draw_line(int x1,int y1,int x2,int y2,int color){
	if(create_type==CREATE_HTML)
		return;
	if( 2 == statusmap_mod){
		fprintf(image_output_file,"<line id=\"l\" x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" stroke=\"#%06x\" />\n",
		x1,y1,x2,y2,color);
	}else{
		gdImageLine(map_image,x1,y1,x2,y2,color);
	}
	return;
	}


/* draws a dotted line */
void draw_dotted_line(int x1,int y1,int x2,int y2,int color){
	if( 2 == statusmap_mod){
		fprintf(image_output_file,"<line id=\"draw_dotted_line\" x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" stroke=\"#%06x\"/>\n",
		x1,y1,x2,y2,color);
	}else{
		int styleDotted[12];
		styleDotted[0]=color;
		styleDotted[1]=gdTransparent;
		styleDotted[2]=gdTransparent;
		styleDotted[3]=gdTransparent;
		styleDotted[4]=gdTransparent;
		styleDotted[5]=gdTransparent;
		styleDotted[6]=color;
		styleDotted[7]=gdTransparent;
		styleDotted[8]=gdTransparent;
		styleDotted[9]=gdTransparent;
		styleDotted[10]=gdTransparent;
		styleDotted[11]=gdTransparent;
		/* sets current style to a dashed line */
		gdImageSetStyle(map_image,styleDotted,12);
		/* draws a line (dotted) */
		gdImageLine(map_image,x1,y1,x2,y2,gdStyled);
	}
	return;
	}

/* draws a dashed line */
void draw_dashed_line(int x1,int y1,int x2,int y2,int color){
	if( 2 == statusmap_mod){
		fprintf(image_output_file,"<line id=\"draw_dashed_line\" x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" stroke=\"#%06x\"/>\n",
		x1,y1,x2,y2,color);
		fflush(image_output_file);
	}else{
		int styleDashed[12];

		styleDashed[0]=color;
		styleDashed[1]=color;
		styleDashed[2]=color;
		styleDashed[3]=color;
		styleDashed[4]=gdTransparent;
		styleDashed[5]=gdTransparent;
		styleDashed[6]=color;
		styleDashed[7]=color;
		styleDashed[8]=color;
		styleDashed[9]=color;
		styleDashed[10]=gdTransparent;
		styleDashed[11]=gdTransparent;

		/* sets current style to a dashed line */
		gdImageSetStyle(map_image,styleDashed,12);

		/* draws a line (dashed) */
		gdImageLine(map_image,x1,y1,x2,y2,gdStyled);
	}
	return;
	}



/******************************************************************/
/*********************** GRAPHICS FUNCTIONS ***********************/
/******************************************************************/

/* initialize graphics */
int initialize_graphics(void){

	/* use STDOUT for writing the image data... */
	image_output_file=stdout;
	scaled_image_width=(int)(DEFAULT_NODE_WIDTH*(double)((double)max_image_width/(double)canvas_width));
	scaled_image_height=(int)(DEFAULT_NODE_HEIGHT*(double)((double)max_image_height/(double)canvas_height));
	scaled_image_width=(scaled_image_width>0)?scaled_image_width:DEFAULT_NODE_WIDTH;
	scaled_image_height=(scaled_image_height>0)?scaled_image_height:DEFAULT_NODE_HEIGHT;
	char image_input_file[MAX_INPUT_BUFFER];

	if(create_type==CREATE_HTML)
		return ERROR;
	if( 2 != statusmap_mod ){ /*is PNG or JPEG mod */
		/* allocate buffer for storing image */
		map_image=gdImageCreate(canvas_width,canvas_height);
		if(map_image==NULL)
			return ERROR;
		/* allocate colors used for drawing */
		color_white=gdImageColorAllocate(map_image,255,255,255);
		color_black=gdImageColorAllocate(map_image,0,0,0);
		color_grey=gdImageColorAllocate(map_image,128,128,128);
		color_lightgrey=gdImageColorAllocate(map_image,210,210,210);
		color_red=gdImageColorAllocate(map_image,255,0,0);
		color_lightred=gdImageColorAllocate(map_image,215,175,175);
		color_green=gdImageColorAllocate(map_image,0,175,0);
		color_lightgreen=gdImageColorAllocate(map_image,210,255,215);
		color_blue=gdImageColorAllocate(map_image,0,0,255);
		color_yellow=gdImageColorAllocate(map_image,255,255,0);
		color_orange=gdImageColorAllocate(map_image,255,100,25);

		/* set transparency index */
		gdImageColorTransparent(map_image,color_white);

		/* make sure the graphic is interlaced */
		gdImageInterlace(map_image,1);

		/* get the path where we will be reading logo images from (GD2 format)... */
		snprintf(physical_logo_images_path,sizeof(physical_logo_images_path)-1,"%slogos/",physical_images_path);
		physical_logo_images_path[sizeof(physical_logo_images_path)-1]='\x0';

		/* load the unknown icon to use for hosts that don't have pretty images associated with them... */
		snprintf(image_input_file,sizeof(image_input_file)-1,"%s%s",physical_logo_images_path,UNKNOWN_GD2_ICON);
		image_input_file[sizeof(image_input_file)-1]='\x0';
		unknown_logo_image=load_image_from_file(image_input_file);
		}
	else{
		snprintf(physical_logo_images_path,sizeof(physical_logo_images_path)-1,"%slogos/",physical_images_path);
		physical_logo_images_path[sizeof(physical_logo_images_path)-1]='\x0';
		/* SVG Header & svg-root node. */
	 	fprintf(image_output_file,"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
		fprintf(image_output_file,"<svg \n  version=\"1.1\" baseProfile=\"tiny\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n");
		fprintf(image_output_file,"  id=\"svg-root\" x=\"%d\" y=\"%d\" width=\"100%\" heigth=\"100%\" viewBox=\"0 0 %d %d\">\n",	canvas_x,canvas_y,canvas_width,canvas_height);
	}
	return OK;
        }



/* loads a graphic image (GD2, JPG or PNG) from file into memory */
gdImagePtr load_image_from_file(char *filename){
	FILE *fp;
	gdImagePtr im=NULL;
	char *ext;

	/* make sure we were passed a file name */
	if(filename==NULL)
		return NULL;
	if( 2 == statusmap_mod)return NULL;
	/* find the file extension */
	if((ext=rindex(filename,'.'))==NULL)
		return NULL;

	/* open the file for reading (binary mode) */
	fp=fopen(filename,"rb");
	if(fp==NULL)
		return NULL;

	/* attempt to read files in various formats */
	if(!strcasecmp(ext,".png"))
		im=gdImageCreateFromPng(fp);
	else if(!strcasecmp(ext,".jpg") || !strcasecmp(ext,".jpeg"))
		im=gdImageCreateFromJpeg(fp);
	else if(!strcasecmp(ext,".xbm"))
		im=gdImageCreateFromXbm(fp);
	else if(!strcasecmp(ext,".gd2"))
		im=gdImageCreateFromGd2(fp);
	else if(!strcasecmp(ext,".gd"))
		im=gdImageCreateFromGd(fp);

	/* fall back to GD2 image format */
	else
		im=gdImageCreateFromGd2(fp);

	/* close the file */
	fclose(fp);

	return im;
        }


/* draw graphics */
void write_graphics(void){
	if(create_type==CREATE_HTML)
		return;
	/* write the image out in PNG format */
	if( 0 == statusmap_mod)
		gdImagePngEx(map_image,image_output_file,1);
	/* or we could write the image out in JPG format... */
	if( 1 == statusmap_mod)
		gdImageJpeg(map_image,image_output_file,30);
	if( 2 == statusmap_mod){
		fprintf(image_output_file,"<rect id=\"test-frame\" x=\"0\" y=\"0\" width=\"%d\" height=\"%d\" fill=\"none\" stroke=\"#0000ff\"/>\n",canvas_width,canvas_height);
		/*fprintf(image_output_file,"<rect id=\"test-frame\" x=\"0\" y=\"0\" width=\"%d\" height=\"%d\" fill=\"none\" stroke=\"#0000ff\"/>\n",canvas_width,canvas_height);*/
		fprintf(image_output_file,"</svg>\n");
		fflush(image_output_file);
	}
	return;
}


/* cleanup graphics resources */
void cleanup_graphics(void){

	if(create_type==CREATE_HTML)
		return;

	/* free memory allocated to image */
	if( 2 != statusmap_mod) gdImageDestroy(map_image);

	return;
        }




/******************************************************************/
/************************* MISC FUNCTIONS *************************/
/******************************************************************/


/* write JavaScript code an layer for popup window */
void write_popup_code(void){
	char *border_color="#000000";
	char *background_color="#ffffcc";
	int border=1;
	int padding=3;
	int x_offset=3;
	int y_offset=3;

	printf("<SCRIPT LANGUAGE='JavaScript'>\n");
	printf("<!--\n");
	printf("// JavaScript popup based on code originally found at http://www.helpmaster.com/htmlhelp/javascript/popjbpopup.htm\n");
	printf("function showPopup(text, eventObj){\n");
	printf("if(!document.all && document.getElementById)\n");
	printf("{ document.all=document.getElementsByTagName(\"*\")}\n");
	printf("ieLayer = 'document.all[\\'popup\\']';\n");
	printf("nnLayer = 'document.layers[\\'popup\\']';\n");
	printf("moLayer = 'document.getElementById(\\'popup\\')';\n");

	printf("if(!(document.all||document.layers||document.documentElement)) return;\n");

	printf("if(document.all) { document.popup=eval(ieLayer); }\n");
	printf("else {\n");
	printf("  if (document.documentElement) document.popup=eval(moLayer);\n");
	printf("  else document.popup=eval(nnLayer);\n");
	printf("}\n");

	printf("var table = \"\";\n");

	printf("if (document.all||document.documentElement){\n");
	printf("table += \"<table bgcolor='%s' border=%d cellpadding=%d cellspacing=0>\";\n",background_color,border,padding);
	printf("table += \"<tr><td>\";\n");
	printf("table += \"<table cellspacing=0 cellpadding=%d>\";\n",padding);
	printf("table += \"<tr><td bgcolor='%s' class='popupText'>\" + text + \"</td></tr>\";\n",background_color);
	printf("table += \"</table></td></tr></table>\"\n");
	printf("document.popup.innerHTML = table;\n");
	printf("document.popup.style.left = document.body.scrollLeft + %d;\n",x_offset);
	printf("document.popup.style.top = document.body.scrollTop + %d;\n",y_offset);
	/*
	printf("document.popup.style.left = (document.all ? eventObj.x : eventObj.layerX) + %d;\n",x_offset);
	printf("document.popup.style.top  = (document.all ? eventObj.y : eventObj.layerY) + %d;\n",y_offset);
	*/

	printf("document.popup.style.visibility = \"visible\";\n");
	printf("} \n");


	printf("else{\n");
	printf("table += \"<table cellpadding=%d border=%d cellspacing=0 bordercolor='%s'>\";\n",padding,border,border_color);
	printf("table += \"<tr><td bgcolor='%s' class='popupText'>\" + text + \"</td></tr></table>\";\n",background_color);
	printf("document.popup.document.open();\n");
	printf("document.popup.document.write(table);\n");
	printf("document.popup.document.close();\n");

	/* set x coordinate */
	printf("document.popup.left = eventObj.layerX + %d;\n",x_offset);

	/* make sure we don't overlap the right side of the screen */
	printf("if(document.popup.left + document.popup.document.width + %d > window.innerWidth) document.popup.left = window.innerWidth - document.popup.document.width - %d - 16;\n",x_offset,x_offset);

	/* set y coordinate */
	printf("document.popup.top  = eventObj.layerY + %d;\n",y_offset);

	/* make sure we don't overlap the bottom edge of the screen */
	printf("if(document.popup.top + document.popup.document.height + %d > window.innerHeight) document.popup.top = window.innerHeight - document.popup.document.height - %d - 16;\n",y_offset,y_offset);

	/* make the popup visible */
	printf("document.popup.visibility = \"visible\";\n");
	printf("}\n");
	printf("}\n");

	printf("function hidePopup(){ \n");
	printf("if (!(document.all || document.layers || document.documentElement)) return;\n");
	printf("if (document.popup == null){ }\n");
	printf("else if (document.all||document.documentElement) document.popup.style.visibility = \"hidden\";\n");
	printf("else document.popup.visibility = \"hidden\";\n");
	printf("document.popup = null;\n");
	printf("}\n");
	printf("//-->\n");

	printf("</SCRIPT>\n");

	return;
        }



/* adds a layer to the list in memory */
int add_layer(char *group_name){
	layer *new_layer;

	if(group_name==NULL)
		return ERROR;

	/* allocate memory for a new layer */
	new_layer=(layer *)malloc(sizeof(layer));
	if(new_layer==NULL)
		return ERROR;

	new_layer->layer_name=(char *)malloc(strlen(group_name)+1);
	if(new_layer->layer_name==NULL){
		free(new_layer);
		return ERROR;
	        }

	strcpy(new_layer->layer_name,group_name);

	/* add new layer to head of layer list */
	new_layer->next=layer_list;
	layer_list=new_layer;

	return OK;
        }



/* frees memory allocated to the layer list */
void free_layer_list(void){
	layer *this_layer;
	layer *next_layer;

	return;

	for(this_layer=layer_list;layer_list!=NULL;this_layer=next_layer){
		next_layer=this_layer->next;
		free(this_layer->layer_name);
		free(this_layer);
	        }

	return;
        }


/* checks to see if a host is in the layer list */
int is_host_in_layer_list(host *hst){
	hostgroup *temp_hostgroup;
	layer *temp_layer;

	if(hst==NULL)
		return FALSE;

	/* check each layer... */
	for(temp_layer=layer_list;temp_layer!=NULL;temp_layer=temp_layer->next){

		/* find the hostgroup */
		temp_hostgroup=find_hostgroup(temp_layer->layer_name);
		if(temp_hostgroup==NULL)
			continue;

		/* is the requested host a member of the hostgroup/layer? */
		if(is_host_member_of_hostgroup(temp_hostgroup,hst)==TRUE)
			return TRUE;
	        }

	return FALSE;
        }


/* print layer url info */
void print_layer_url(int get_method){
	layer *temp_layer;

	for(temp_layer=layer_list;temp_layer!=NULL;temp_layer=temp_layer->next){
		if(get_method==TRUE)
			printf("&layer=%s",temp_layer->layer_name);
		else
			printf("<input type='hidden' name='layer' value='%s'>\n",temp_layer->layer_name);
	        }

	if(get_method==TRUE)
		printf("&layermode=%s",(exclude_layers==TRUE)?"exclude":"include");
	else
		printf("<input type='hidden' name='layermode' value='%s'>\n",(exclude_layers==TRUE)?"exclude":"include");

	return;
        }




/******************************************************************/
/************************ UTILITY FUNCTIONS ***********************/
/******************************************************************/

/* calculates how many "layers" separate parent and child - used by collapsed tree layout method */
int host_child_depth_separation(host *parent, host *child){
	int this_depth=0;
	int min_depth=0;
	int have_min_depth=FALSE;
	host *temp_host;

	if(child==NULL)
		return -1;

	if(parent==child)
		return 0;

	if(is_host_immediate_child_of_host(parent,child)==TRUE)
		return 1;

	for(temp_host=host_list;temp_host!=NULL;temp_host=temp_host->next){

		if(is_host_immediate_child_of_host(parent,temp_host)==TRUE){

			this_depth=host_child_depth_separation(temp_host,child);

			if(this_depth>=0 && (have_min_depth==FALSE || (have_min_depth==TRUE && (this_depth<min_depth)))){
				have_min_depth=TRUE;
				min_depth=this_depth;
			        }
		        }
	        }

	if(have_min_depth==FALSE)
		return -1;
	else
		return min_depth+1;
        }



/* calculates how many hosts reside on a specific "layer" - used by collapsed tree layout method */
int number_of_host_layer_members(host *parent, int layer){
	int current_layer;
	int layer_members=0;
	host *temp_host;

	for(temp_host=host_list;temp_host!=NULL;temp_host=temp_host->next){

		current_layer=host_child_depth_separation(parent,temp_host);

		if(current_layer==layer)
			layer_members++;
	        }

	return layer_members;
        }



/* calculate max number of members on all "layers" beneath and including parent host - used by collapsed tree layout method */
int max_child_host_layer_members(host *parent){
	int current_layer;
	int max_members=1;
	int current_members=0;

	for(current_layer=1;;current_layer++){

		current_members=number_of_host_layer_members(parent,current_layer);

		if(current_members<=0)
			break;

		if(current_members>max_members)
			max_members=current_members;
	        }

	return max_members;
        }



/* calculate max drawing width for host and children - used by balanced tree layout method */
int max_child_host_drawing_width(host *parent){
	host *temp_host;
	int child_width=0;


	for(temp_host=host_list;temp_host!=NULL;temp_host=temp_host->next){

		if(is_host_immediate_child_of_host(parent,temp_host)==TRUE)
			child_width+=max_child_host_drawing_width(temp_host);
	        }

	/* no children, so set width to 1 for this host */
	if(child_width==0)
		return 1;

	else
		return child_width;
        }



/* calculates number of services associated with a particular service */
int number_of_host_services(host *hst){
	service *temp_service;
	int total_services=0;

	if(hst==NULL)
		return 0;

	/* check all the services */
	for(temp_service=service_list;temp_service!=NULL;temp_service=temp_service->next){
		if(!strcmp(temp_service->host_name,hst->name))
			total_services++;
	        }

	return total_services;
        }



/******************************************************************/
/***************** COORDINATE CALCULATION FUNCTIONS ***************/
/******************************************************************/

/* calculates coords of a host's children - used by balanced tree layout method */
void calculate_balanced_tree_coords(host *parent, int x, int y){
	int parent_drawing_width;
	int start_drawing_x;
	int current_drawing_x;
	int this_drawing_width;
	host *temp_host;
	hostextinfo *temp_hostextinfo;

	/* calculate total drawing width of parent host */
	parent_drawing_width=max_child_host_drawing_width(parent);

	/* calculate starting x coord */
	start_drawing_x=x-(((DEFAULT_NODE_WIDTH*parent_drawing_width)+(DEFAULT_NODE_HSPACING*(parent_drawing_width-1)))/2);
	current_drawing_x=start_drawing_x;


	/* calculate coords for children */
	for(temp_host=host_list;temp_host!=NULL;temp_host=temp_host->next){

		temp_hostextinfo=find_hostextinfo(temp_host->name);
		if(temp_hostextinfo==NULL)
			continue;

		if(is_host_immediate_child_of_host(parent,temp_host)==TRUE){

			/* get drawing width of child host */
			this_drawing_width=max_child_host_drawing_width(temp_host);

			temp_hostextinfo->x_2d=current_drawing_x+(((DEFAULT_NODE_WIDTH*this_drawing_width)+(DEFAULT_NODE_HSPACING*(this_drawing_width-1)))/2);
			temp_hostextinfo->y_2d=y+DEFAULT_NODE_HEIGHT+DEFAULT_NODE_VSPACING;
			temp_hostextinfo->have_2d_coords=TRUE;
			temp_hostextinfo->should_be_drawn=TRUE;

			current_drawing_x+=(this_drawing_width*DEFAULT_NODE_WIDTH)+((this_drawing_width-1)*DEFAULT_NODE_HSPACING)+DEFAULT_NODE_HSPACING;

			/* recurse into child host ... */
			calculate_balanced_tree_coords(temp_host,temp_hostextinfo->x_2d,temp_hostextinfo->y_2d);
		        }

	        }

	return;
        }


/* calculate coords of all hosts in circular layout method */
void calculate_circular_coords(void){
	int min_x=0;
	int min_y=0;
	int have_min_x=FALSE;
	int have_min_y=FALSE;
	hostextinfo *temp_hostextinfo;

	/* calculate all host coords, starting with first layer */
	calculate_circular_layer_coords(NULL,0.0,360.0,1,CIRCULAR_DRAWING_RADIUS);

	/* adjust all calculated coords so none are negative in x or y axis... */

	/* calculate min x, y coords */
	for(temp_hostextinfo=hostextinfo_list;temp_hostextinfo!=NULL;temp_hostextinfo=temp_hostextinfo->next){
		if(have_min_x==FALSE || temp_hostextinfo->x_2d<min_x){
			have_min_x=TRUE;
			min_x=temp_hostextinfo->x_2d;
		        }
		if(have_min_y==FALSE || temp_hostextinfo->y_2d<min_y){
			have_min_y=TRUE;
			min_y=temp_hostextinfo->y_2d;
		        }
	        }

	/* offset all drawing coords by the min x,y coords we found */
	for(temp_hostextinfo=hostextinfo_list;temp_hostextinfo!=NULL;temp_hostextinfo=temp_hostextinfo->next){
		if(min_x<0)
			temp_hostextinfo->x_2d-=min_x;
		if(min_y<0)
			temp_hostextinfo->y_2d-=min_y;
	        }

	if(min_x<0)
		nagios_icon_x-=min_x;
	if(min_y<0)
		nagios_icon_y-=min_y;

	for(temp_hostextinfo=hostextinfo_list;temp_hostextinfo!=NULL;temp_hostextinfo=temp_hostextinfo->next){
		temp_hostextinfo->x_2d+=(DEFAULT_NODE_WIDTH/2);
		temp_hostextinfo->y_2d+=(DEFAULT_NODE_HEIGHT/2);
	        }
	nagios_icon_x+=(DEFAULT_NODE_WIDTH/2);
	nagios_icon_y+=(DEFAULT_NODE_HEIGHT/2);

	return;
        }


/* calculates coords of all hosts in a particular "layer" in circular layout method */
void calculate_circular_layer_coords(host *parent, double start_angle, double useable_angle, int layer, int radius){
	int parent_drawing_width=0;
	int this_drawing_width=0;
	int immediate_children=0;
	double current_drawing_angle=0.0;
	double this_drawing_angle=0.0;
	double available_angle=0.0;
	double clipped_available_angle=0.0;
	double average_child_angle=0.0;
	double x_coord=0.0;
	double y_coord=0.0;
	host *temp_host;
	hostextinfo *temp_hostextinfo;


	/* get the total number of immediate children to this host */
	immediate_children=number_of_immediate_child_hosts(parent);

	/* bail out if we're done */
	if(immediate_children==0)
		return;

	/* calculate total drawing "width" of parent host */
	parent_drawing_width=max_child_host_drawing_width(parent);

	/* calculate average angle given to each child host */
	average_child_angle=(double)(useable_angle/(double)immediate_children);

	/* calculate initial drawing angle */
	current_drawing_angle=start_angle;


	/* calculate coords for children */
	for(temp_host=host_list;temp_host!=NULL;temp_host=temp_host->next){

		temp_hostextinfo=find_hostextinfo(temp_host->name);
		if(temp_hostextinfo==NULL)
			continue;

		if(is_host_immediate_child_of_host(parent,temp_host)==TRUE){

			/* get drawing width of child host */
			this_drawing_width=max_child_host_drawing_width(temp_host);

			/* calculate angle this host gets for drawing */
			available_angle=useable_angle*((double)this_drawing_width/(double)parent_drawing_width);

			/* clip available angle if necessary */
			/* this isn't really necessary, but helps keep things looking a bit more sane with less potential connection crossover */
			clipped_available_angle=360.0/layer;
			if(available_angle<clipped_available_angle)
				clipped_available_angle=available_angle;

			/* calculate the exact angle at which we should draw this child */
			this_drawing_angle=current_drawing_angle+(available_angle/2.0);

			/* compensate for angle overflow */
			while(this_drawing_angle>=360.0)
				this_drawing_angle-=360.0;
			while(this_drawing_angle<0.0)
				this_drawing_angle+=360.0;

			/* calculate drawing coords of this host using good ol' geometry... */
			x_coord=-(sin(-this_drawing_angle*(M_PI/180.0))*radius);
			y_coord=-(sin((90+this_drawing_angle)*(M_PI/180.0))*radius);

			temp_hostextinfo->x_2d=(int)x_coord;
			temp_hostextinfo->y_2d=(int)y_coord;
			temp_hostextinfo->have_2d_coords=TRUE;
			temp_hostextinfo->should_be_drawn=TRUE;

			/* recurse into child host ... */
			calculate_circular_layer_coords(temp_host,current_drawing_angle+((available_angle-clipped_available_angle)/2),clipped_available_angle,layer+1,radius+CIRCULAR_DRAWING_RADIUS);

			/* increment current drawing angle */
			current_drawing_angle+=available_angle;
		        }
	        }

	return;
        }



/* draws background "extras" for all hosts in circular markup layout */
void draw_circular_markup(void){

	/* calculate all host sections, starting with first layer */
	draw_circular_layer_markup(NULL,0.0,360.0,1,CIRCULAR_DRAWING_RADIUS);

	return;
        }


/* draws background "extras" for all hosts in a particular "layer" in circular markup layout */
void draw_circular_layer_markup(host *parent, double start_angle, double useable_angle, int layer, int radius){
	int parent_drawing_width=0;
	int this_drawing_width=0;
	int immediate_children=0;
	double current_drawing_angle=0.0;
	double available_angle=0.0;
	double clipped_available_angle=0.0;
	double average_child_angle=0.0;
	double x_coord[4]={0.0,0.0,0.0,0.0};
	double y_coord[4]={0.0,0.0,0.0,0.0};
	host *temp_host;
	hoststatus *temp_hoststatus;
	hostextinfo *temp_hostextinfo;
	int x_offset=0;
	int y_offset=0;
	int center_x=0;
	int center_y=0;
	int bgcolor=0;
	double arc_start_angle=0.0;
	double arc_end_angle=0.0;
	int translated_x=0;
	int translated_y=0;

	/* get the total number of immediate children to this host */
	immediate_children=number_of_immediate_child_hosts(parent);

	/* bail out if we're done */
	if(immediate_children==0)
		return;

	/* calculate total drawing "width" of parent host */
	parent_drawing_width=max_child_host_drawing_width(parent);

	/* calculate average angle given to each child host */
	average_child_angle=(double)(useable_angle/(double)immediate_children);

	/* calculate initial drawing angle */
	current_drawing_angle=start_angle;

	/* calculate coords for children */
	for(temp_host=host_list;temp_host!=NULL;temp_host=temp_host->next){

		temp_hostextinfo=find_hostextinfo(temp_host->name);
		if(temp_hostextinfo==NULL)
			continue;

		if(is_host_immediate_child_of_host(parent,temp_host)==TRUE){

			/* get drawing width of child host */
			this_drawing_width=max_child_host_drawing_width(temp_host);

			/* calculate angle this host gets for drawing */
			available_angle=useable_angle*((double)this_drawing_width/(double)parent_drawing_width);

			/* clip available angle if necessary */
			/* this isn't really necessary, but helps keep things looking a bit more sane with less potential connection crossover */
			clipped_available_angle=360.0/layer;
			if(available_angle<clipped_available_angle)
				clipped_available_angle=available_angle;

			/* calculate drawing coords of "leftmost" divider using good ol' geometry... */
			x_coord[0]=-(sin(-current_drawing_angle*(M_PI/180.0))*(radius-(CIRCULAR_DRAWING_RADIUS/2)));
			y_coord[0]=-(sin((90+current_drawing_angle)*(M_PI/180.0))*(radius-(CIRCULAR_DRAWING_RADIUS/2)));
			x_coord[1]=-(sin(-current_drawing_angle*(M_PI/180.0))*(radius+(CIRCULAR_DRAWING_RADIUS/2)));
			y_coord[1]=-(sin((90+current_drawing_angle)*(M_PI/180.0))*(radius+(CIRCULAR_DRAWING_RADIUS/2)));

			/* calculate drawing coords of "rightmost" divider using good ol' geometry... */
			x_coord[2]=-(sin((-(current_drawing_angle+available_angle))*(M_PI/180.0))*(radius-(CIRCULAR_DRAWING_RADIUS/2)));
			y_coord[2]=-(sin((90+current_drawing_angle+available_angle)*(M_PI/180.0))*(radius-(CIRCULAR_DRAWING_RADIUS/2)));
			x_coord[3]=-(sin((-(current_drawing_angle+available_angle))*(M_PI/180.0))*(radius+(CIRCULAR_DRAWING_RADIUS/2)));
			y_coord[3]=-(sin((90+current_drawing_angle+available_angle)*(M_PI/180.0))*(radius+(CIRCULAR_DRAWING_RADIUS/2)));


			x_offset=nagios_icon_x+(DEFAULT_NODE_WIDTH/2)-canvas_x;
			y_offset=nagios_icon_y+(DEFAULT_NODE_HEIGHT/2)-canvas_y;

			/* draw "slice" dividers */
			if(immediate_children>1 || layer>1){

				/* draw "leftmost" divider */
				gdImageLine(map_image,(int)x_coord[0]+x_offset,(int)y_coord[0]+y_offset,(int)x_coord[1]+x_offset,(int)y_coord[1]+y_offset,color_lightgrey);

				/* draw "rightmost" divider */
				gdImageLine(map_image,(int)x_coord[2]+x_offset,(int)y_coord[2]+y_offset,(int)x_coord[3]+x_offset,(int)y_coord[3]+y_offset,color_lightgrey);
			        }

			/* determine arc drawing angles */
			arc_start_angle=current_drawing_angle-90.0;
			while(arc_start_angle<0.0)
				arc_start_angle+=360.0;
			arc_end_angle=arc_start_angle+available_angle;

			/* draw inner arc */
			gdImageArc(map_image,x_offset,y_offset,(radius-(CIRCULAR_DRAWING_RADIUS/2))*2,(radius-(CIRCULAR_DRAWING_RADIUS/2))*2,floor(arc_start_angle),ceil(arc_end_angle),color_lightgrey);

			/* draw outer arc */
			gdImageArc(map_image,x_offset,y_offset,(radius+(CIRCULAR_DRAWING_RADIUS/2))*2,(radius+(CIRCULAR_DRAWING_RADIUS/2))*2,floor(arc_start_angle),ceil(arc_end_angle),color_lightgrey);


			/* determine center of "slice" and fill with appropriate color */
			center_x=-(sin(-(current_drawing_angle+(available_angle/2.0))*(M_PI/180.0))*(radius));
			center_y=-(sin((90+current_drawing_angle+(available_angle/2.0))*(M_PI/180.0))*(radius));
			translated_x=center_x+x_offset;
			translated_y=center_y+y_offset;

			/* determine background color */
			temp_hoststatus=find_hoststatus(temp_host->name);
			if(temp_hoststatus==NULL)
				bgcolor=color_lightgrey;
			else if(temp_hoststatus->status==HOST_DOWN || temp_hoststatus->status==HOST_UNREACHABLE)
				bgcolor=color_lightred;
			else
				bgcolor=color_lightgreen;

			/* fill slice with background color */
			/* the fill function only works with coordinates that are in bounds of the actual image */
			if(translated_x>0 && translated_y>0 && translated_x<canvas_width && translated_y<canvas_height)
				gdImageFillToBorder(map_image,translated_x,translated_y,color_lightgrey,bgcolor);

			/* recurse into child host ... */
			draw_circular_layer_markup(temp_host,current_drawing_angle+((available_angle-clipped_available_angle)/2),clipped_available_angle,layer+1,radius+CIRCULAR_DRAWING_RADIUS);

			/* increment current drawing angle */
			current_drawing_angle+=available_angle;
		        }
	        }

	return;
        }

